forked from Plemya-x/ALR
		
	Merge pull request 'feat: add support for multiple packages in one alr.sh' (#35) from Maks1mS/ALR:feat/add-multiple-package-build into master
Reviewed-on: Plemya-x/ALR#35 В этом PR добавлена поддержка сборки нескольких пакетов из одного alr.sh, которая скорее всего потребует доработки в дальнейшем. Пример репозитория с пакетами, содержащие несколько package_: https://gitea.plemya-x.ru/Maks1mS/multipackage-test-repo
This commit is contained in:
		
							
								
								
									
										79
									
								
								build.go
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								build.go
									
									
									
									
									
								
							| @@ -23,14 +23,17 @@ import ( | ||||
| 	"log/slog" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
|  | ||||
| 	"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/osutils" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" | ||||
| ) | ||||
| @@ -46,6 +49,11 @@ func BuildCmd() *cli.Command { | ||||
| 				Value:   "alr.sh", | ||||
| 				Usage:   gotext.Get("Path to the build script"), | ||||
| 			}, | ||||
| 			&cli.StringFlag{ | ||||
| 				Name:    "script-package", | ||||
| 				Aliases: []string{"sp"}, | ||||
| 				Usage:   gotext.Get("Specify package in script (for multi package script only)"), | ||||
| 			}, | ||||
| 			&cli.StringFlag{ | ||||
| 				Name:    "package", | ||||
| 				Aliases: []string{"p"}, | ||||
| @@ -59,30 +67,58 @@ func BuildCmd() *cli.Command { | ||||
| 		}, | ||||
| 		Action: func(c *cli.Context) error { | ||||
| 			ctx := c.Context | ||||
| 			cfg := config.New() | ||||
| 			db := database.New(cfg) | ||||
| 			rs := repos.New(cfg, db) | ||||
| 			err := db.Init(ctx) | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error db init"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			var script string | ||||
| 			var packages []string | ||||
|  | ||||
| 			// Проверяем, установлен ли флаг script (-s) | ||||
|  | ||||
| 			repoDir := cfg.GetPaths(ctx).RepoDir | ||||
|  | ||||
| 			switch { | ||||
| 			case c.IsSet("script"): | ||||
| 				script = c.String("script") | ||||
| 				packages = append(packages, c.String("script-package")) | ||||
| 			case c.IsSet("package"): | ||||
| 				// TODO: handle multiple packages | ||||
| 				packageInput := c.String("package") | ||||
| 				if filepath.Dir(packageInput) == "." { | ||||
| 					// Не указана директория репозитория, используем 'default' как префикс | ||||
| 					script = filepath.Join(config.GetPaths(ctx).RepoDir, "default", packageInput, "alr.sh") | ||||
|  | ||||
| 				arr := strings.Split(packageInput, "/") | ||||
| 				var packageSearch string | ||||
| 				if len(arr) == 2 { | ||||
| 					packageSearch = arr[1] | ||||
| 				} else { | ||||
| 					// Используем путь с указанным репозиторием | ||||
| 					script = filepath.Join(config.GetPaths(ctx).RepoDir, packageInput, "alr.sh") | ||||
| 					packageSearch = arr[0] | ||||
| 				} | ||||
|  | ||||
| 				pkgs, _, _ := rs.FindPkgs(ctx, []string{packageSearch}) | ||||
| 				pkg, ok := pkgs[packageSearch] | ||||
| 				if len(pkg) < 1 || !ok { | ||||
| 					slog.Error(gotext.Get("Package not found")) | ||||
| 					os.Exit(1) | ||||
| 				} | ||||
|  | ||||
| 				if pkg[0].BasePkgName != "" { | ||||
| 					script = filepath.Join(repoDir, pkg[0].Repository, pkg[0].BasePkgName, "alr.sh") | ||||
| 					packages = append(packages, pkg[0].Name) | ||||
| 				} else { | ||||
| 					script = filepath.Join(repoDir, pkg[0].Repository, pkg[0].Name, "alr.sh") | ||||
| 				} | ||||
| 			default: | ||||
| 				script = filepath.Join(config.GetPaths(ctx).RepoDir, "alr.sh") | ||||
| 				script = filepath.Join(repoDir, "alr.sh") | ||||
| 			} | ||||
|  | ||||
| 			// Проверка автоматического пулла репозиториев | ||||
| 			if config.GetInstance(ctx).AutoPull(ctx) { | ||||
| 				err := repos.Pull(ctx, config.Config(ctx).Repos) | ||||
| 			if cfg.AutoPull(ctx) { | ||||
| 				err := rs.Pull(ctx, cfg.Repos(ctx)) | ||||
| 				if err != nil { | ||||
| 					slog.Error(gotext.Get("Error pulling repositories"), "err", err) | ||||
| 					os.Exit(1) | ||||
| @@ -96,13 +132,28 @@ func BuildCmd() *cli.Command { | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			info, err := distro.ParseOSRelease(ctx) | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error parsing os release"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			builder := build.NewBuilder( | ||||
| 				ctx, | ||||
| 				types.BuildOpts{ | ||||
| 					Packages:    packages, | ||||
| 					Script:      script, | ||||
| 					Manager:     mgr, | ||||
| 					Clean:       c.Bool("clean"), | ||||
| 					Interactive: c.Bool("interactive"), | ||||
| 				}, | ||||
| 				rs, | ||||
| 				info, | ||||
| 				cfg, | ||||
| 			) | ||||
|  | ||||
| 			// Сборка пакета | ||||
| 			pkgPaths, _, err := build.BuildPackage(ctx, types.BuildOpts{ | ||||
| 				Script:      script, | ||||
| 				Manager:     mgr, | ||||
| 				Clean:       c.Bool("clean"), | ||||
| 				Interactive: c.Bool("interactive"), | ||||
| 			}) | ||||
| 			pkgPaths, _, err := builder.BuildPackage(ctx) | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error building package"), "err", err) | ||||
| 				os.Exit(1) | ||||
|   | ||||
| @@ -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.2%</text> | ||||
|         <text x="86" y="14">19.2%</text> | ||||
|         <text x="86" y="15" fill="#010101" fill-opacity=".3">20.8%</text> | ||||
|         <text x="86" y="14">20.8%</text> | ||||
|     </g> | ||||
| </svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 926 B After Width: | Height: | Size: 926 B | 
							
								
								
									
										45
									
								
								install.go
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								install.go
									
									
									
									
									
								
							| @@ -29,9 +29,10 @@ import ( | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||
| 	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/pkg/build" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" | ||||
| ) | ||||
| @@ -63,22 +64,52 @@ func InstallCmd() *cli.Command { | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			if config.GetInstance(ctx).AutoPull(ctx) { | ||||
| 				err := repos.Pull(ctx, config.Config(ctx).Repos) | ||||
| 			cfg := config.New() | ||||
| 			db := database.New(cfg) | ||||
| 			rs := repos.New(cfg, db) | ||||
| 			err := db.Init(ctx) | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error db init"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			if cfg.AutoPull(ctx) { | ||||
| 				err := rs.Pull(ctx, cfg.Repos(ctx)) | ||||
| 				if err != nil { | ||||
| 					slog.Error(gotext.Get("Error pulling repositories"), "err", err) | ||||
| 					os.Exit(1) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			found, notFound, err := repos.FindPkgs(ctx, args.Slice()) | ||||
| 			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")) | ||||
| 			build.InstallPkgs(ctx, pkgs, notFound, types.BuildOpts{ | ||||
|  | ||||
| 			opts := types.BuildOpts{ | ||||
| 				Manager:     mgr, | ||||
| 				Clean:       c.Bool("clean"), | ||||
| 				Interactive: c.Bool("interactive"), | ||||
| 			} | ||||
|  | ||||
| 			info, err := distro.ParseOSRelease(ctx) | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error parsing os release"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			builder := build.NewBuilder( | ||||
| 				ctx, | ||||
| 				opts, | ||||
| 				rs, | ||||
| 				info, | ||||
| 				cfg, | ||||
| 			) | ||||
|  | ||||
| 			builder.InstallPkgs(ctx, pkgs, notFound, types.BuildOpts{ | ||||
| 				Manager:     mgr, | ||||
| 				Clean:       c.Bool("clean"), | ||||
| 				Interactive: c.Bool("interactive"), | ||||
| @@ -86,6 +117,8 @@ func InstallCmd() *cli.Command { | ||||
| 			return nil | ||||
| 		}, | ||||
| 		BashComplete: func(c *cli.Context) { | ||||
| 			cfg := config.New() | ||||
| 			db := database.New(cfg) | ||||
| 			result, err := db.GetPkgs(c.Context, "true") | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error getting packages"), "err", err) | ||||
| @@ -94,7 +127,7 @@ func InstallCmd() *cli.Command { | ||||
| 			defer result.Close() | ||||
|  | ||||
| 			for result.Next() { | ||||
| 				var pkg db.Package | ||||
| 				var pkg database.Package | ||||
| 				err = result.StructScan(&pkg) | ||||
| 				if err != nil { | ||||
| 					slog.Error(gotext.Get("Error iterating over packages"), "err", err) | ||||
|   | ||||
| @@ -170,3 +170,10 @@ func (c *ALRConfig) AutoPull(ctx context.Context) bool { | ||||
| 	}) | ||||
| 	return c.cfg.AutoPull | ||||
| } | ||||
|  | ||||
| func (c *ALRConfig) PagerStyle(ctx context.Context) string { | ||||
| 	c.cfgOnce.Do(func() { | ||||
| 		c.Load(ctx) | ||||
| 	}) | ||||
| 	return c.cfg.PagerStyle | ||||
| } | ||||
|   | ||||
| @@ -31,10 +31,11 @@ import ( | ||||
|  | ||||
| // CurrentVersion is the current version of the database. | ||||
| // The database is reset if its version doesn't match this. | ||||
| const CurrentVersion = 2 | ||||
| const CurrentVersion = 3 | ||||
|  | ||||
| // Package is a ALR package's database representation | ||||
| type Package struct { | ||||
| 	BasePkgName   string                    `sh:"base" db:"basepkg_name"` | ||||
| 	Name          string                    `sh:"name,required" db:"name"` | ||||
| 	Version       string                    `sh:"version,required" db:"version"` | ||||
| 	Release       int                       `sh:"release,required" db:"release"` | ||||
| @@ -99,6 +100,7 @@ func (d *Database) initDB(ctx context.Context) error { | ||||
| 	conn := d.conn | ||||
| 	_, err := conn.ExecContext(ctx, ` | ||||
| 		CREATE TABLE IF NOT EXISTS pkgs ( | ||||
| 			basepkg_name  TEXT NOT NULL, | ||||
| 			name          TEXT NOT NULL, | ||||
| 			repository    TEXT NOT NULL, | ||||
| 			version       TEXT NOT NULL, | ||||
| @@ -196,6 +198,7 @@ func (d *Database) IsEmpty(ctx context.Context) bool { | ||||
| func (d *Database) InsertPackage(ctx context.Context, pkg Package) error { | ||||
| 	_, err := d.conn.NamedExecContext(ctx, ` | ||||
| 		INSERT OR REPLACE INTO pkgs ( | ||||
| 			basepkg_name, | ||||
| 			name, | ||||
| 			repository, | ||||
| 			version, | ||||
| @@ -213,6 +216,7 @@ func (d *Database) InsertPackage(ctx context.Context, pkg Package) error { | ||||
| 			builddepends, | ||||
| 			optdepends | ||||
| 		) VALUES ( | ||||
| 		 	:basepkg_name, | ||||
| 			:name, | ||||
| 			:repository, | ||||
| 			:version, | ||||
|   | ||||
| @@ -123,6 +123,7 @@ func (FileDownloader) Download(ctx context.Context, opts Options) (Type, string, | ||||
| 	} else { | ||||
| 		out = fl | ||||
| 	} | ||||
| 	defer out.Close() | ||||
|  | ||||
| 	h, err := opts.NewHash() | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -164,7 +164,10 @@ func (d *Decoder) DecodeVars(val any) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type ScriptFunc func(ctx context.Context, opts ...interp.RunnerOption) error | ||||
| type ( | ||||
| 	ScriptFunc             func(ctx context.Context, opts ...interp.RunnerOption) error | ||||
| 	ScriptFuncWithSubshell func(ctx context.Context, opts ...interp.RunnerOption) (*interp.Runner, error) | ||||
| ) | ||||
|  | ||||
| // GetFunc returns a function corresponding to a bash function | ||||
| // with the given name | ||||
| @@ -197,6 +200,24 @@ func (d *Decoder) GetFuncP(name string, prepare PrepareFunc) (ScriptFunc, bool) | ||||
| 	}, true | ||||
| } | ||||
|  | ||||
| func (d *Decoder) GetFuncWithSubshell(name string) (ScriptFuncWithSubshell, bool) { | ||||
| 	fn := d.getFunc(name) | ||||
| 	if fn == nil { | ||||
| 		return nil, false | ||||
| 	} | ||||
|  | ||||
| 	return func(ctx context.Context, opts ...interp.RunnerOption) (*interp.Runner, error) { | ||||
| 		sub := d.Runner.Subshell() | ||||
| 		for _, opt := range opts { | ||||
| 			err := opt(sub) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 		return sub, sub.Run(ctx, fn) | ||||
| 	}, true | ||||
| } | ||||
|  | ||||
| func (d *Decoder) getFunc(name string) *syntax.Stmt { | ||||
| 	names, err := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name)) | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -29,9 +29,9 @@ import ( | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/fakeroot" | ||||
| 	"mvdan.cc/sh/v3/expand" | ||||
| 	"mvdan.cc/sh/v3/interp" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/fakeroot" | ||||
| ) | ||||
|  | ||||
| // FakerootExecHandler was extracted from github.com/mvdan/sh/interp/handler.go | ||||
|   | ||||
| @@ -9,40 +9,56 @@ msgstr "" | ||||
| "Content-Transfer-Encoding: 8bit\n" | ||||
| "Plural-Forms: nplurals=2; plural=(n != 1);\n" | ||||
|  | ||||
| #: build.go:41 | ||||
| #: build.go:44 | ||||
| msgid "Build a local package" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:47 | ||||
| #: build.go:50 | ||||
| msgid "Path to the build script" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:52 | ||||
| #: build.go:55 | ||||
| msgid "Specify package in script (for multi package script only)" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:60 | ||||
| msgid "Name of the package to build and its repo (example: default/go-bin)" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:57 | ||||
| #: build.go:65 | ||||
| msgid "" | ||||
| "Build package from scratch even if there's an already built package available" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:87 | ||||
| msgid "Error pulling repositories" | ||||
| #: build.go:75 | ||||
| msgid "Error db init" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:95 | ||||
| msgid "Unable to detect a supported package manager on the system" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:107 | ||||
| msgid "Error building package" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:114 | ||||
| msgid "Error getting working directory" | ||||
| #: build.go:105 | ||||
| msgid "Package not found" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:123 | ||||
| msgid "Error pulling repositories" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:131 | ||||
| msgid "Unable to detect a supported package manager on the system" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:137 | ||||
| msgid "Error parsing os release" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:158 | ||||
| msgid "Error building package" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:165 | ||||
| msgid "Error getting working directory" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:174 | ||||
| msgid "Error moving the package" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -130,31 +146,31 @@ msgstr "" | ||||
| msgid "Error encoding script variables" | ||||
| msgstr "" | ||||
|  | ||||
| #: install.go:42 | ||||
| #: install.go:43 | ||||
| msgid "Install a new package" | ||||
| msgstr "" | ||||
|  | ||||
| #: install.go:56 | ||||
| #: install.go:57 | ||||
| msgid "Command install expected at least 1 argument, got %d" | ||||
| msgstr "" | ||||
|  | ||||
| #: install.go:91 | ||||
| #: install.go:124 | ||||
| msgid "Error getting packages" | ||||
| msgstr "" | ||||
|  | ||||
| #: install.go:100 | ||||
| #: install.go:133 | ||||
| msgid "Error iterating over packages" | ||||
| msgstr "" | ||||
|  | ||||
| #: install.go:113 | ||||
| #: install.go:146 | ||||
| msgid "Remove an installed package" | ||||
| msgstr "" | ||||
|  | ||||
| #: install.go:118 | ||||
| #: install.go:151 | ||||
| msgid "Command remove expected at least 1 argument, got %d" | ||||
| msgstr "" | ||||
|  | ||||
| #: install.go:130 | ||||
| #: install.go:163 | ||||
| msgid "Error removing packages" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -222,11 +238,11 @@ msgstr "" | ||||
| msgid "Error parsing system language" | ||||
| msgstr "" | ||||
|  | ||||
| #: internal/db/db.go:131 | ||||
| #: internal/db/db.go:133 | ||||
| msgid "Database version mismatch; resetting" | ||||
| msgstr "" | ||||
|  | ||||
| #: internal/db/db.go:138 | ||||
| #: internal/db/db.go:140 | ||||
| msgid "" | ||||
| "Database version does not exist. Run alr fix if something isn't working." | ||||
| msgstr "" | ||||
| @@ -293,82 +309,70 @@ msgstr "" | ||||
| msgid "Error while running app" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:108 | ||||
| #: pkg/build/build.go:153 | ||||
| msgid "Failed to prompt user to view build script" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:112 | ||||
| #: pkg/build/build.go:157 | ||||
| msgid "Building package" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:156 | ||||
| #: pkg/build/build.go:228 | ||||
| msgid "Downloading sources" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:168 | ||||
| #: pkg/build/build.go:246 | ||||
| msgid "Building package metadata" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:190 | ||||
| #: pkg/build/build.go:268 | ||||
| msgid "Compressing package" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:316 | ||||
| #: pkg/build/build.go:419 | ||||
| msgid "" | ||||
| "Your system's CPU architecture doesn't match this package. Do you want to " | ||||
| "build anyway?" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:327 | ||||
| #: pkg/build/build.go:433 | ||||
| msgid "This package is already installed" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:355 | ||||
| #: pkg/build/build.go:457 | ||||
| msgid "Installing build dependencies" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:397 | ||||
| #: pkg/build/build.go:498 | ||||
| msgid "Installing dependencies" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:443 | ||||
| msgid "Executing version()" | ||||
| #: pkg/build/build.go:533 | ||||
| msgid "The checksums array must be the same length as sources" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:463 | ||||
| msgid "Updating version" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:468 | ||||
| msgid "Executing prepare()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:478 | ||||
| msgid "Executing build()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:488 | ||||
| msgid "Executing package()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:510 | ||||
| msgid "Executing files()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:588 | ||||
| msgid "AutoProv is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:599 | ||||
| msgid "AutoReq is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:706 | ||||
| #: pkg/build/build.go:584 | ||||
| msgid "Would you like to remove the build dependencies?" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:812 | ||||
| msgid "The checksums array must be the same length as sources" | ||||
| #: pkg/build/build.go:647 | ||||
| msgid "Executing prepare()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:657 | ||||
| msgid "Executing build()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:687 pkg/build/build.go:707 | ||||
| msgid "Executing %s()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:766 | ||||
| msgid "Error installing native packages" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:790 | ||||
| msgid "Error installing package" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/findDeps.go:35 | ||||
| @@ -383,27 +387,27 @@ msgstr "" | ||||
| msgid "Required dependency found" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/install.go:42 | ||||
| msgid "Error installing native packages" | ||||
| #: pkg/build/utils.go:133 | ||||
| msgid "AutoProv is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/install.go:79 | ||||
| msgid "Error installing package" | ||||
| #: pkg/build/utils.go:144 | ||||
| msgid "AutoReq is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/repos/pull.go:75 | ||||
| #: pkg/repos/pull.go:79 | ||||
| msgid "Pulling repository" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/repos/pull.go:99 | ||||
| #: pkg/repos/pull.go:103 | ||||
| msgid "Repository up to date" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/repos/pull.go:156 | ||||
| #: pkg/repos/pull.go:160 | ||||
| msgid "Git repository does not appear to be a valid ALR repo" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/repos/pull.go:172 | ||||
| #: pkg/repos/pull.go:176 | ||||
| msgid "" | ||||
| "ALR repo's minimum ALR version is greater than the current version. Try " | ||||
| "updating ALR if something doesn't work." | ||||
| @@ -457,10 +461,10 @@ msgstr "" | ||||
| msgid "Upgrade all installed packages" | ||||
| msgstr "" | ||||
|  | ||||
| #: upgrade.go:83 | ||||
| #: upgrade.go:90 | ||||
| msgid "Error checking for updates" | ||||
| msgstr "" | ||||
|  | ||||
| #: upgrade.go:94 | ||||
| #: upgrade.go:112 | ||||
| msgid "There is nothing to do." | ||||
| msgstr "" | ||||
|   | ||||
| @@ -16,40 +16,57 @@ msgstr "" | ||||
| "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" | ||||
| "X-Generator: Gtranslator 47.1\n" | ||||
|  | ||||
| #: build.go:41 | ||||
| #: build.go:44 | ||||
| msgid "Build a local package" | ||||
| msgstr "Сборка локального пакета" | ||||
|  | ||||
| #: build.go:47 | ||||
| #: build.go:50 | ||||
| msgid "Path to the build script" | ||||
| msgstr "Путь к скрипту сборки" | ||||
|  | ||||
| #: build.go:52 | ||||
| #: build.go:55 | ||||
| msgid "Specify package in script (for multi package script only)" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:60 | ||||
| msgid "Name of the package to build and its repo (example: default/go-bin)" | ||||
| msgstr "Имя пакета для сборки и его репозиторий (пример: default/go-bin)" | ||||
|  | ||||
| #: build.go:57 | ||||
| #: build.go:65 | ||||
| msgid "" | ||||
| "Build package from scratch even if there's an already built package available" | ||||
| msgstr "Создайте пакет с нуля, даже если уже имеется готовый пакет" | ||||
|  | ||||
| #: build.go:87 | ||||
| #: build.go:75 | ||||
| msgid "Error db init" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:105 | ||||
| msgid "Package not found" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:123 | ||||
| msgid "Error pulling repositories" | ||||
| msgstr "Ошибка при извлечении репозиториев" | ||||
|  | ||||
| #: build.go:95 | ||||
| #: build.go:131 | ||||
| msgid "Unable to detect a supported package manager on the system" | ||||
| msgstr "Не удалось обнаружить поддерживаемый менеджер пакетов в системе" | ||||
|  | ||||
| #: build.go:107 | ||||
| #: build.go:137 | ||||
| #, fuzzy | ||||
| msgid "Error parsing os release" | ||||
| msgstr "Ошибка при разборе файла выпуска операционной системы" | ||||
|  | ||||
| #: build.go:158 | ||||
| msgid "Error building package" | ||||
| msgstr "Ошибка при сборке пакета" | ||||
|  | ||||
| #: build.go:114 | ||||
| #: build.go:165 | ||||
| msgid "Error getting working directory" | ||||
| msgstr "Ошибка при получении рабочего каталога" | ||||
|  | ||||
| #: build.go:123 | ||||
| #: build.go:174 | ||||
| msgid "Error moving the package" | ||||
| msgstr "Ошибка при перемещении пакета" | ||||
|  | ||||
| @@ -137,31 +154,31 @@ msgstr "Ошибка устранения переорпеделений" | ||||
| msgid "Error encoding script variables" | ||||
| msgstr "Ошибка кодирования переменных скрита" | ||||
|  | ||||
| #: install.go:42 | ||||
| #: install.go:43 | ||||
| msgid "Install a new package" | ||||
| msgstr "Установить новый пакет" | ||||
|  | ||||
| #: install.go:56 | ||||
| #: install.go:57 | ||||
| msgid "Command install expected at least 1 argument, got %d" | ||||
| msgstr "Для команды install ожидался хотя бы 1 аргумент, получено %d" | ||||
|  | ||||
| #: install.go:91 | ||||
| #: install.go:124 | ||||
| msgid "Error getting packages" | ||||
| msgstr "Ошибка при получении пакетов" | ||||
|  | ||||
| #: install.go:100 | ||||
| #: install.go:133 | ||||
| msgid "Error iterating over packages" | ||||
| msgstr "Ошибка при переборе пакетов" | ||||
|  | ||||
| #: install.go:113 | ||||
| #: install.go:146 | ||||
| msgid "Remove an installed package" | ||||
| msgstr "Удалить установленный пакет" | ||||
|  | ||||
| #: install.go:118 | ||||
| #: install.go:151 | ||||
| msgid "Command remove expected at least 1 argument, got %d" | ||||
| msgstr "Для команды remove ожидался хотя бы 1 аргумент, получено %d" | ||||
|  | ||||
| #: install.go:130 | ||||
| #: install.go:163 | ||||
| msgid "Error removing packages" | ||||
| msgstr "Ошибка при удалении пакетов" | ||||
|  | ||||
| @@ -233,11 +250,11 @@ msgstr "Не удалось создать каталог кэша пакето | ||||
| msgid "Error parsing system language" | ||||
| msgstr "Ошибка при парсинге языка системы" | ||||
|  | ||||
| #: internal/db/db.go:131 | ||||
| #: internal/db/db.go:133 | ||||
| msgid "Database version mismatch; resetting" | ||||
| msgstr "Несоответствие версий базы данных; сброс настроек" | ||||
|  | ||||
| #: internal/db/db.go:138 | ||||
| #: internal/db/db.go:140 | ||||
| msgid "" | ||||
| "Database version does not exist. Run alr fix if something isn't working." | ||||
| msgstr "" | ||||
| @@ -307,27 +324,27 @@ msgstr "" | ||||
| msgid "Error while running app" | ||||
| msgstr "Ошибка при запуске приложения" | ||||
|  | ||||
| #: pkg/build/build.go:108 | ||||
| #: pkg/build/build.go:153 | ||||
| msgid "Failed to prompt user to view build script" | ||||
| msgstr "Не удалось предложить пользователю просмотреть скрипт сборки" | ||||
|  | ||||
| #: pkg/build/build.go:112 | ||||
| #: pkg/build/build.go:157 | ||||
| msgid "Building package" | ||||
| msgstr "Сборка пакета" | ||||
|  | ||||
| #: pkg/build/build.go:156 | ||||
| #: pkg/build/build.go:228 | ||||
| msgid "Downloading sources" | ||||
| msgstr "Скачивание источников" | ||||
|  | ||||
| #: pkg/build/build.go:168 | ||||
| #: pkg/build/build.go:246 | ||||
| msgid "Building package metadata" | ||||
| msgstr "Сборка метаданных пакета" | ||||
|  | ||||
| #: pkg/build/build.go:190 | ||||
| #: pkg/build/build.go:268 | ||||
| msgid "Compressing package" | ||||
| msgstr "Сжатие пакета" | ||||
|  | ||||
| #: pkg/build/build.go:316 | ||||
| #: pkg/build/build.go:419 | ||||
| msgid "" | ||||
| "Your system's CPU architecture doesn't match this package. Do you want to " | ||||
| "build anyway?" | ||||
| @@ -335,59 +352,46 @@ msgstr "" | ||||
| "Архитектура процессора вашей системы не соответствует этому пакету. Вы все " | ||||
| "равно хотите выполнить сборку?" | ||||
|  | ||||
| #: pkg/build/build.go:327 | ||||
| #: pkg/build/build.go:433 | ||||
| msgid "This package is already installed" | ||||
| msgstr "Этот пакет уже установлен" | ||||
|  | ||||
| #: pkg/build/build.go:355 | ||||
| #: pkg/build/build.go:457 | ||||
| msgid "Installing build dependencies" | ||||
| msgstr "Установка зависимостей сборки" | ||||
|  | ||||
| #: pkg/build/build.go:397 | ||||
| #: pkg/build/build.go:498 | ||||
| msgid "Installing dependencies" | ||||
| msgstr "Установка зависимостей" | ||||
|  | ||||
| #: pkg/build/build.go:443 | ||||
| msgid "Executing version()" | ||||
| msgstr "Исполнение версия()" | ||||
| #: pkg/build/build.go:533 | ||||
| msgid "The checksums array must be the same length as sources" | ||||
| msgstr "Массив контрольных сумм должен быть той же длины, что и источники" | ||||
|  | ||||
| #: pkg/build/build.go:463 | ||||
| msgid "Updating version" | ||||
| msgstr "Обновление версии" | ||||
|  | ||||
| #: pkg/build/build.go:468 | ||||
| msgid "Executing prepare()" | ||||
| msgstr "Исполнение prepare()" | ||||
|  | ||||
| #: pkg/build/build.go:478 | ||||
| msgid "Executing build()" | ||||
| msgstr "Исполнение build()" | ||||
|  | ||||
| #: pkg/build/build.go:488 | ||||
| msgid "Executing package()" | ||||
| msgstr "Исполнение package()" | ||||
|  | ||||
| #: pkg/build/build.go:510 | ||||
| msgid "Executing files()" | ||||
| msgstr "Исполнение files()" | ||||
|  | ||||
| #: pkg/build/build.go:588 | ||||
| msgid "AutoProv is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
| "AutoProv не реализовано для этого формата пакета, поэтому будет пропущено" | ||||
|  | ||||
| #: pkg/build/build.go:599 | ||||
| msgid "AutoReq is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
| "AutoReq не реализовано для этого формата пакета, поэтому будет пропущено" | ||||
|  | ||||
| #: pkg/build/build.go:706 | ||||
| #: pkg/build/build.go:584 | ||||
| msgid "Would you like to remove the build dependencies?" | ||||
| msgstr "Хотели бы вы удалить зависимости сборки?" | ||||
|  | ||||
| #: pkg/build/build.go:812 | ||||
| msgid "The checksums array must be the same length as sources" | ||||
| msgstr "Массив контрольных сумм должен быть той же длины, что и источники" | ||||
| #: pkg/build/build.go:647 | ||||
| msgid "Executing prepare()" | ||||
| msgstr "Исполнение prepare()" | ||||
|  | ||||
| #: pkg/build/build.go:657 | ||||
| msgid "Executing build()" | ||||
| msgstr "Исполнение build()" | ||||
|  | ||||
| #: pkg/build/build.go:687 pkg/build/build.go:707 | ||||
| #, fuzzy | ||||
| msgid "Executing %s()" | ||||
| msgstr "Исполнение files()" | ||||
|  | ||||
| #: pkg/build/build.go:766 | ||||
| msgid "Error installing native packages" | ||||
| msgstr "Ошибка при установке нативных пакетов" | ||||
|  | ||||
| #: pkg/build/build.go:790 | ||||
| msgid "Error installing package" | ||||
| msgstr "Ошибка при установке пакета" | ||||
|  | ||||
| #: pkg/build/findDeps.go:35 | ||||
| msgid "Command not found on the system" | ||||
| @@ -401,27 +405,29 @@ msgstr "Найденная предоставленная зависимость | ||||
| msgid "Required dependency found" | ||||
| msgstr "Найдена требуемая зависимость" | ||||
|  | ||||
| #: pkg/build/install.go:42 | ||||
| msgid "Error installing native packages" | ||||
| msgstr "Ошибка при установке нативных пакетов" | ||||
| #: pkg/build/utils.go:133 | ||||
| msgid "AutoProv is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
| "AutoProv не реализовано для этого формата пакета, поэтому будет пропущено" | ||||
|  | ||||
| #: pkg/build/install.go:79 | ||||
| msgid "Error installing package" | ||||
| msgstr "Ошибка при установке пакета" | ||||
| #: pkg/build/utils.go:144 | ||||
| msgid "AutoReq is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
| "AutoReq не реализовано для этого формата пакета, поэтому будет пропущено" | ||||
|  | ||||
| #: pkg/repos/pull.go:75 | ||||
| #: pkg/repos/pull.go:79 | ||||
| msgid "Pulling repository" | ||||
| msgstr "Скачивание репозитория" | ||||
|  | ||||
| #: pkg/repos/pull.go:99 | ||||
| #: pkg/repos/pull.go:103 | ||||
| msgid "Repository up to date" | ||||
| msgstr "Репозиторий уже обновлён" | ||||
|  | ||||
| #: pkg/repos/pull.go:156 | ||||
| #: pkg/repos/pull.go:160 | ||||
| msgid "Git repository does not appear to be a valid ALR repo" | ||||
| msgstr "Репозиторий Git не поддерживается репозиторием ALR" | ||||
|  | ||||
| #: pkg/repos/pull.go:172 | ||||
| #: pkg/repos/pull.go:176 | ||||
| msgid "" | ||||
| "ALR repo's minimum ALR version is greater than the current version. Try " | ||||
| "updating ALR if something doesn't work." | ||||
| @@ -477,10 +483,19 @@ msgstr "Скачать все изменённые репозитории" | ||||
| msgid "Upgrade all installed packages" | ||||
| msgstr "Обновить все установленные пакеты" | ||||
|  | ||||
| #: upgrade.go:83 | ||||
| #: upgrade.go:90 | ||||
| msgid "Error checking for updates" | ||||
| msgstr "Ошибка при проверке обновлений" | ||||
|  | ||||
| #: upgrade.go:94 | ||||
| #: upgrade.go:112 | ||||
| msgid "There is nothing to do." | ||||
| msgstr "Здесь нечего делать." | ||||
|  | ||||
| #~ msgid "Executing version()" | ||||
| #~ msgstr "Исполнение версия()" | ||||
|  | ||||
| #~ msgid "Updating version" | ||||
| #~ msgstr "Обновление версии" | ||||
|  | ||||
| #~ msgid "Executing package()" | ||||
| #~ msgstr "Исполнение package()" | ||||
|   | ||||
| @@ -23,11 +23,61 @@ import "gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||
|  | ||||
| type BuildOpts struct { | ||||
| 	Script      string | ||||
| 	Packages    []string | ||||
| 	Manager     manager.Manager | ||||
| 	Clean       bool | ||||
| 	Interactive bool | ||||
| } | ||||
|  | ||||
| type BuildVarsPre struct { | ||||
| 	Version       string   `sh:"version,required"` | ||||
| 	Release       int      `sh:"release,required"` | ||||
| 	Epoch         uint     `sh:"epoch"` | ||||
| 	Description   string   `sh:"desc"` | ||||
| 	Homepage      string   `sh:"homepage"` | ||||
| 	Maintainer    string   `sh:"maintainer"` | ||||
| 	Architectures []string `sh:"architectures"` | ||||
| 	Licenses      []string `sh:"license"` | ||||
| 	Provides      []string `sh:"provides"` | ||||
| 	Conflicts     []string `sh:"conflicts"` | ||||
| 	Depends       []string `sh:"deps"` | ||||
| 	BuildDepends  []string `sh:"build_deps"` | ||||
| 	OptDepends    []string `sh:"opt_deps"` | ||||
| 	Replaces      []string `sh:"replaces"` | ||||
| 	Sources       []string `sh:"sources"` | ||||
| 	Checksums     []string `sh:"checksums"` | ||||
| 	Backup        []string `sh:"backup"` | ||||
| 	Scripts       Scripts  `sh:"scripts"` | ||||
| 	AutoReq       []string `sh:"auto_req"` | ||||
| 	AutoProv      []string `sh:"auto_prov"` | ||||
| } | ||||
|  | ||||
| func (bv *BuildVarsPre) ToBuildVars() BuildVars { | ||||
| 	return BuildVars{ | ||||
| 		Name:          "", | ||||
| 		Version:       bv.Version, | ||||
| 		Release:       bv.Release, | ||||
| 		Epoch:         bv.Epoch, | ||||
| 		Description:   bv.Description, | ||||
| 		Homepage:      bv.Homepage, | ||||
| 		Maintainer:    bv.Maintainer, | ||||
| 		Architectures: bv.Architectures, | ||||
| 		Licenses:      bv.Licenses, | ||||
| 		Provides:      bv.Provides, | ||||
| 		Conflicts:     bv.Conflicts, | ||||
| 		Depends:       bv.Depends, | ||||
| 		BuildDepends:  bv.BuildDepends, | ||||
| 		OptDepends:    bv.OptDepends, | ||||
| 		Replaces:      bv.Replaces, | ||||
| 		Sources:       bv.Sources, | ||||
| 		Checksums:     bv.Checksums, | ||||
| 		Backup:        bv.Backup, | ||||
| 		Scripts:       bv.Scripts, | ||||
| 		AutoReq:       bv.AutoReq, | ||||
| 		AutoProv:      bv.AutoProv, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // BuildVars represents the script variables required | ||||
| // to build a package | ||||
| type BuildVars struct { | ||||
|   | ||||
							
								
								
									
										1086
									
								
								pkg/build/build.go
									
									
									
									
									
								
							
							
						
						
									
										1086
									
								
								pkg/build/build.go
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -18,10 +18,18 @@ package build | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"mvdan.cc/sh/v3/syntax" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||
| ) | ||||
|  | ||||
| @@ -134,93 +142,145 @@ func (m *TestManager) IsInstalled(pkg string) (bool, error) { | ||||
| 	return true, nil | ||||
| } | ||||
|  | ||||
| // TODO: fix test | ||||
| func TestInstallBuildDeps(t *testing.T) { | ||||
| 	type testEnv struct { | ||||
| 		pf   PackageFinder | ||||
| 		vars *types.BuildVars | ||||
| 		opts types.BuildOpts | ||||
| type TestConfig struct{} | ||||
|  | ||||
| 		// Contains pkgs captured by FindPkgsFunc | ||||
| 		// capturedPkgs []string | ||||
| func (c *TestConfig) PagerStyle(ctx context.Context) string { | ||||
| 	return "native" | ||||
| } | ||||
|  | ||||
| func (c *TestConfig) GetPaths(ctx context.Context) *config.Paths { | ||||
| 	return &config.Paths{ | ||||
| 		CacheDir: "/tmp", | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestExecuteFirstPassIsSecure(t *testing.T) { | ||||
| 	cfg := &TestConfig{} | ||||
| 	pf := &TestPackageFinder{} | ||||
| 	info := &distro.OSRelease{} | ||||
| 	m := &TestManager{} | ||||
|  | ||||
| 	opts := types.BuildOpts{ | ||||
| 		Manager:     m, | ||||
| 		Interactive: false, | ||||
| 	} | ||||
|  | ||||
| 	ctx := context.Background() | ||||
|  | ||||
| 	b := NewBuilder( | ||||
| 		ctx, | ||||
| 		opts, | ||||
| 		pf, | ||||
| 		info, | ||||
| 		cfg, | ||||
| 	) | ||||
|  | ||||
| 	tmpFile, err := os.CreateTemp("", "testfile-") | ||||
| 	assert.NoError(t, err) | ||||
| 	tmpFilePath := tmpFile.Name() | ||||
| 	defer os.Remove(tmpFilePath) | ||||
|  | ||||
| 	_, err = os.Stat(tmpFilePath) | ||||
| 	assert.NoError(t, err) | ||||
|  | ||||
| 	testScript := fmt.Sprintf(`name='test' | ||||
| version=1.0.0 | ||||
| release=1 | ||||
| rm -f %s`, tmpFilePath) | ||||
|  | ||||
| 	fl, err := syntax.NewParser().Parse(strings.NewReader(testScript), "alr.sh") | ||||
| 	assert.NoError(t, err) | ||||
|  | ||||
| 	_, _, err = b.executeFirstPass(fl) | ||||
| 	assert.NoError(t, err) | ||||
|  | ||||
| 	_, err = os.Stat(tmpFilePath) | ||||
| 	assert.NoError(t, err) | ||||
| } | ||||
|  | ||||
| func TestExecuteFirstPassIsCorrect(t *testing.T) { | ||||
| 	type testCase struct { | ||||
| 		Name     string | ||||
| 		Prepare  func() *testEnv | ||||
| 		Expected func(t *testing.T, e *testEnv, res []string, err error) | ||||
| 		Script   string | ||||
| 		Opts     types.BuildOpts | ||||
| 		Expected func(t *testing.T, vars []*types.BuildVars) | ||||
| 	} | ||||
|  | ||||
| 	for _, tc := range []testCase{ | ||||
| 		/* | ||||
| 			{ | ||||
| 				Name: "install only needed deps", | ||||
| 				Prepare: func() *testEnv { | ||||
| 					pf := TestPackageFinder{} | ||||
| 					vars := types.BuildVars{} | ||||
| 					m := TestManager{} | ||||
| 					opts := types.BuildOpts{ | ||||
| 						Manager:     &m, | ||||
| 						Interactive: false, | ||||
| 					} | ||||
| 	for _, tc := range []testCase{{ | ||||
| 		Name: "single package", | ||||
| 		Script: `name='test' | ||||
| version='1.0.0' | ||||
| release=1 | ||||
| epoch=2 | ||||
| desc="Test package" | ||||
| homepage='https://example.com' | ||||
| maintainer='Ivan Ivanov' | ||||
| `, | ||||
| 		Opts: types.BuildOpts{ | ||||
| 			Manager:     &TestManager{}, | ||||
| 			Interactive: false, | ||||
| 		}, | ||||
| 		Expected: func(t *testing.T, vars []*types.BuildVars) { | ||||
| 			assert.Equal(t, 1, len(vars)) | ||||
| 			assert.Equal(t, vars[0].Name, "test") | ||||
| 			assert.Equal(t, vars[0].Version, "1.0.0") | ||||
| 			assert.Equal(t, vars[0].Release, int(1)) | ||||
| 			assert.Equal(t, vars[0].Epoch, uint(2)) | ||||
| 			assert.Equal(t, vars[0].Description, "Test package") | ||||
| 		}, | ||||
| 	}, { | ||||
| 		Name: "multiple packages", | ||||
| 		Script: `name=( | ||||
| 	foo | ||||
| 	bar | ||||
| ) | ||||
|  | ||||
| 					env := &testEnv{ | ||||
| 						pf:           &pf, | ||||
| 						vars:         &vars, | ||||
| 						opts:         opts, | ||||
| 						capturedPkgs: []string{}, | ||||
| 					} | ||||
| version='0.0.1' | ||||
| release=1 | ||||
| epoch=2 | ||||
| desc="Test package" | ||||
|  | ||||
| 					pf.FindPkgsFunc = func(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error) { | ||||
| 						env.capturedPkgs = append(env.capturedPkgs, pkgs...) | ||||
| 						result := make(map[string][]db.Package) | ||||
| 						result["bar"] = []db.Package{{ | ||||
| 							Name: "bar-pkg", | ||||
| 						}} | ||||
| 						result["buz"] = []db.Package{{ | ||||
| 							Name: "buz-pkg", | ||||
| 						}} | ||||
| meta_foo() { | ||||
| 	desc="Foo package" | ||||
| } | ||||
|  | ||||
| 						return result, []string{}, nil | ||||
| 					} | ||||
| meta_bar() { | ||||
|  | ||||
| 					vars.BuildDepends = []string{ | ||||
| 						"foo", | ||||
| 						"bar", | ||||
| 						"buz", | ||||
| 					} | ||||
| 					m.IsInstalledFunc = func(pkg string) (bool, error) { | ||||
| 						if pkg == "foo" { | ||||
| 							return true, nil | ||||
| 						} else { | ||||
| 							return false, nil | ||||
| 						} | ||||
| 					} | ||||
| } | ||||
| `, | ||||
| 		Opts: types.BuildOpts{ | ||||
| 			Packages:    []string{"foo"}, | ||||
| 			Manager:     &TestManager{}, | ||||
| 			Interactive: false, | ||||
| 		}, | ||||
| 		Expected: func(t *testing.T, vars []*types.BuildVars) { | ||||
| 			assert.Equal(t, 1, len(vars)) | ||||
| 			assert.Equal(t, vars[0].Name, "foo") | ||||
| 			assert.Equal(t, vars[0].Description, "Foo package") | ||||
| 		}, | ||||
| 	}} { | ||||
| 		t.Run(tc.Name, func(t *testing.T) { | ||||
| 			cfg := &TestConfig{} | ||||
| 			pf := &TestPackageFinder{} | ||||
| 			info := &distro.OSRelease{} | ||||
|  | ||||
| 					return env | ||||
| 				}, | ||||
| 				Expected: func(t *testing.T, e *testEnv, res []string, err error) { | ||||
| 					assert.NoError(t, err) | ||||
| 					assert.Len(t, res, 2) | ||||
| 					assert.ElementsMatch(t, res, []string{"bar-pkg", "buz-pkg"}) | ||||
|  | ||||
| 					assert.ElementsMatch(t, e.capturedPkgs, []string{"bar", "buz"}) | ||||
| 				}, | ||||
| 			}, | ||||
| 		*/ | ||||
| 	} { | ||||
| 		t.Run(tc.Name, func(tt *testing.T) { | ||||
| 			ctx := context.Background() | ||||
| 			env := tc.Prepare() | ||||
|  | ||||
| 			result, err := installBuildDeps( | ||||
| 			b := NewBuilder( | ||||
| 				ctx, | ||||
| 				env.pf, | ||||
| 				env.vars, | ||||
| 				env.opts, | ||||
| 				tc.Opts, | ||||
| 				pf, | ||||
| 				info, | ||||
| 				cfg, | ||||
| 			) | ||||
|  | ||||
| 			tc.Expected(tt, env, result, err) | ||||
| 			fl, err := syntax.NewParser().Parse(strings.NewReader(tc.Script), "alr.sh") | ||||
| 			assert.NoError(t, err) | ||||
|  | ||||
| 			_, allVars, err := b.executeFirstPass(fl) | ||||
| 			assert.NoError(t, err) | ||||
|  | ||||
| 			tc.Expected(t, allVars) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,84 +0,0 @@ | ||||
| // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||
| // It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов. | ||||
| // | ||||
| // 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" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/leonelquinteros/gotext" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||
| ) | ||||
|  | ||||
| // InstallPkgs устанавливает нативные пакеты с использованием менеджера пакетов, | ||||
| // затем строит и устанавливает пакеты ALR | ||||
| func InstallPkgs(ctx context.Context, alrPkgs []db.Package, nativePkgs []string, opts types.BuildOpts) { | ||||
| 	if len(nativePkgs) > 0 { | ||||
| 		err := opts.Manager.Install(nil, nativePkgs...) | ||||
| 		// Если есть нативные пакеты, выполняем их установку | ||||
| 		if err != nil { | ||||
| 			slog.Error(gotext.Get("Error installing native packages"), "err", err) | ||||
| 			os.Exit(1) | ||||
| 			// Логируем и завершаем выполнение при ошибке | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	InstallScripts(ctx, GetScriptPaths(ctx, alrPkgs), opts) | ||||
| 	// Устанавливаем скрипты сборки через функцию InstallScripts | ||||
| } | ||||
|  | ||||
| // GetScriptPaths возвращает срез путей к скриптам, соответствующий | ||||
| // данным пакетам | ||||
| func GetScriptPaths(ctx context.Context, pkgs []db.Package) []string { | ||||
| 	var scripts []string | ||||
| 	for _, pkg := range pkgs { | ||||
| 		// Для каждого пакета создаем путь к скрипту сборки | ||||
| 		scriptPath := filepath.Join(config.GetPaths(ctx).RepoDir, pkg.Repository, pkg.Name, "alr.sh") | ||||
| 		scripts = append(scripts, scriptPath) | ||||
| 	} | ||||
| 	return scripts | ||||
| } | ||||
|  | ||||
| // InstallScripts строит и устанавливает переданные alr скрипты сборки | ||||
| func InstallScripts(ctx context.Context, scripts []string, opts types.BuildOpts) { | ||||
| 	for _, script := range scripts { | ||||
| 		opts.Script = script // Устанавливаем текущий скрипт в опции | ||||
| 		builtPkgs, _, err := BuildPackage(ctx, opts) | ||||
| 		// Выполняем сборку пакета | ||||
| 		if err != nil { | ||||
| 			slog.Error(gotext.Get("Error building package"), "err", err) | ||||
| 			os.Exit(1) | ||||
| 			// Логируем и завершаем выполнение при ошибке сборки | ||||
| 		} | ||||
|  | ||||
| 		err = opts.Manager.InstallLocal(nil, builtPkgs...) | ||||
| 		// Устанавливаем локально собранные пакеты | ||||
| 		if err != nil { | ||||
| 			slog.Error(gotext.Get("Error installing package"), "err", err) | ||||
| 			os.Exit(1) | ||||
| 			// Логируем и завершаем выполнение при ошибке установки | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										423
									
								
								pkg/build/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										423
									
								
								pkg/build/utils.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,423 @@ | ||||
| // 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" | ||||
| 	"io" | ||||
| 	"log/slog" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"runtime" | ||||
| 	"slices" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	// Импортируем пакеты для поддержки различных форматов пакетов (APK, DEB, RPM и ARCH). | ||||
|  | ||||
| 	_ "github.com/goreleaser/nfpm/v2/apk" | ||||
| 	_ "github.com/goreleaser/nfpm/v2/arch" | ||||
| 	_ "github.com/goreleaser/nfpm/v2/deb" | ||||
| 	_ "github.com/goreleaser/nfpm/v2/rpm" | ||||
| 	"github.com/leonelquinteros/gotext" | ||||
| 	"mvdan.cc/sh/v3/syntax" | ||||
|  | ||||
| 	"github.com/goreleaser/nfpm/v2" | ||||
| 	"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/shutils/decoder" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||
| ) | ||||
|  | ||||
| // Функция readScript анализирует скрипт сборки с использованием встроенной реализации bash | ||||
| func readScript(script string) (*syntax.File, error) { | ||||
| 	fl, err := os.Open(script) // Открываем файл скрипта | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer fl.Close() // Закрываем файл после выполнения | ||||
|  | ||||
| 	file, err := syntax.NewParser().Parse(fl, "alr.sh") // Парсим скрипт с помощью синтаксического анализатора | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return file, nil // Возвращаем синтаксическое дерево | ||||
| } | ||||
|  | ||||
| // Функция prepareDirs подготавливает директории для сборки. | ||||
| func prepareDirs(dirs types.Directories) error { | ||||
| 	err := os.RemoveAll(dirs.BaseDir) // Удаляем базовую директорию, если она существует | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = os.MkdirAll(dirs.SrcDir, 0o755) // Создаем директорию для источников | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return os.MkdirAll(dirs.PkgDir, 0o755) // Создаем директорию для пакетов | ||||
| } | ||||
|  | ||||
| // Функция buildPkgMetadata создает метаданные для пакета, который будет собран. | ||||
| func buildPkgMetadata( | ||||
| 	ctx context.Context, | ||||
| 	vars *types.BuildVars, | ||||
| 	dirs types.Directories, | ||||
| 	pkgFormat string, | ||||
| 	info *distro.OSRelease, | ||||
| 	deps []string, | ||||
| 	preferedContents *[]string, | ||||
| ) (*nfpm.Info, error) { | ||||
| 	pkgInfo := getBasePkgInfo(vars, info) | ||||
| 	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: vars.Conflicts, | ||||
| 		Replaces:  vars.Replaces, | ||||
| 		Provides:  vars.Provides, | ||||
| 		Depends:   deps, | ||||
| 	} | ||||
|  | ||||
| 	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]) { | ||||
| 		if pkgFormat == "rpm" { | ||||
| 			err = rpmFindProvides(ctx, pkgInfo, dirs) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} else { | ||||
| 			slog.Info(gotext.Get("AutoProv is not implemented for this package format, so it's skipped")) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(vars.AutoReq) == 1 && decoder.IsTruthy(vars.AutoReq[0]) { | ||||
| 		if pkgFormat == "rpm" { | ||||
| 			err = rpmFindRequires(ctx, pkgInfo, dirs) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} else { | ||||
| 			slog.Info(gotext.Get("AutoReq is not implemented for this package format, so it's skipped")) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return pkgInfo, nil | ||||
| } | ||||
|  | ||||
| // Функция buildContents создает секцию содержимого пакета, которая содержит файлы, | ||||
| // которые будут включены в конечный пакет. | ||||
| func buildContents(vars *types.BuildVars, dirs types.Directories, preferedContents *[]string) ([]*files.Content, error) { | ||||
| 	contents := []*files.Content{} | ||||
|  | ||||
| 	processPath := func(path, trimmed string, prefered bool) error { | ||||
| 		fi, err := os.Lstat(path) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		if fi.IsDir() { | ||||
| 			f, err := os.Open(path) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			defer f.Close() | ||||
|  | ||||
| 			if !prefered { | ||||
| 				_, err = f.Readdirnames(1) | ||||
| 				if err != io.EOF { | ||||
| 					return nil | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			contents = append(contents, &files.Content{ | ||||
| 				Source:      path, | ||||
| 				Destination: trimmed, | ||||
| 				Type:        "dir", | ||||
| 				FileInfo: &files.ContentFileInfo{ | ||||
| 					MTime: fi.ModTime(), | ||||
| 				}, | ||||
| 			}) | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		if fi.Mode()&os.ModeSymlink != 0 { | ||||
| 			link, err := os.Readlink(path) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			link = strings.TrimPrefix(link, dirs.PkgDir) | ||||
|  | ||||
| 			contents = append(contents, &files.Content{ | ||||
| 				Source:      link, | ||||
| 				Destination: trimmed, | ||||
| 				Type:        "symlink", | ||||
| 				FileInfo: &files.ContentFileInfo{ | ||||
| 					MTime: fi.ModTime(), | ||||
| 					Mode:  fi.Mode(), | ||||
| 				}, | ||||
| 			}) | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		fileContent := &files.Content{ | ||||
| 			Source:      path, | ||||
| 			Destination: trimmed, | ||||
| 			FileInfo: &files.ContentFileInfo{ | ||||
| 				MTime: fi.ModTime(), | ||||
| 				Mode:  fi.Mode(), | ||||
| 				Size:  fi.Size(), | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		if slices.Contains(vars.Backup, trimmed) { | ||||
| 			fileContent.Type = "config|noreplace" | ||||
| 		} | ||||
|  | ||||
| 		contents = append(contents, fileContent) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if preferedContents != nil { | ||||
| 		for _, trimmed := range *preferedContents { | ||||
| 			path := filepath.Join(dirs.PkgDir, trimmed) | ||||
| 			if err := processPath(path, trimmed, true); err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		err := filepath.Walk(dirs.PkgDir, func(path string, fi os.FileInfo, err error) error { | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			trimmed := strings.TrimPrefix(path, dirs.PkgDir) | ||||
| 			return processPath(path, trimmed, false) | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return contents, nil | ||||
| } | ||||
|  | ||||
| // Функция checkForBuiltPackage пытается обнаружить ранее собранный пакет и вернуть его путь | ||||
| // и true, если нашла. Если нет, возвратит "", false, nil. | ||||
| func checkForBuiltPackage( | ||||
| 	mgr manager.Manager, | ||||
| 	vars *types.BuildVars, | ||||
| 	pkgFormat, | ||||
| 	baseDir string, | ||||
| 	info *distro.OSRelease, | ||||
| ) (string, bool, error) { | ||||
| 	filename, err := pkgFileName(vars, pkgFormat, info) | ||||
| 	if err != nil { | ||||
| 		return "", false, err | ||||
| 	} | ||||
|  | ||||
| 	pkgPath := filepath.Join(baseDir, filename) | ||||
|  | ||||
| 	_, err = os.Stat(pkgPath) | ||||
| 	if err != nil { | ||||
| 		return "", false, nil | ||||
| 	} | ||||
|  | ||||
| 	return pkgPath, true, nil | ||||
| } | ||||
|  | ||||
| func getBasePkgInfo(vars *types.BuildVars, info *distro.OSRelease) *nfpm.Info { | ||||
| 	return &nfpm.Info{ | ||||
| 		Name:    vars.Name, | ||||
| 		Arch:    cpu.Arch(), | ||||
| 		Version: vars.Version, | ||||
| 		Release: overrides.ReleasePlatformSpecific(vars.Release, info), | ||||
| 		Epoch:   strconv.FormatUint(uint64(vars.Epoch), 10), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // pkgFileName returns the filename of the package if it were to be built. | ||||
| // This is used to check if the package has already been built. | ||||
| func pkgFileName(vars *types.BuildVars, pkgFormat string, info *distro.OSRelease) (string, error) { | ||||
| 	pkgInfo := getBasePkgInfo(vars, info) | ||||
|  | ||||
| 	packager, err := nfpm.Get(pkgFormat) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	return packager.ConventionalFileName(pkgInfo), nil | ||||
| } | ||||
|  | ||||
| // Функция getPkgFormat возвращает формат пакета из менеджера пакетов, | ||||
| // или ALR_PKG_FORMAT, если он установлен. | ||||
| func getPkgFormat(mgr manager.Manager) string { | ||||
| 	pkgFormat := mgr.Format() | ||||
| 	if format, ok := os.LookupEnv("ALR_PKG_FORMAT"); ok { | ||||
| 		pkgFormat = format | ||||
| 	} | ||||
| 	return pkgFormat | ||||
| } | ||||
|  | ||||
| // Функция createBuildEnvVars создает переменные окружения, которые будут установлены | ||||
| // в скрипте сборки при его выполнении. | ||||
| func createBuildEnvVars(info *distro.OSRelease, dirs types.Directories) []string { | ||||
| 	env := os.Environ() | ||||
|  | ||||
| 	env = append( | ||||
| 		env, | ||||
| 		"DISTRO_NAME="+info.Name, | ||||
| 		"DISTRO_PRETTY_NAME="+info.PrettyName, | ||||
| 		"DISTRO_ID="+info.ID, | ||||
| 		"DISTRO_VERSION_ID="+info.VersionID, | ||||
| 		"DISTRO_ID_LIKE="+strings.Join(info.Like, " "), | ||||
| 		"ARCH="+cpu.Arch(), | ||||
| 		"NCPU="+strconv.Itoa(runtime.NumCPU()), | ||||
| 	) | ||||
|  | ||||
| 	if dirs.ScriptDir != "" { | ||||
| 		env = append(env, "scriptdir="+dirs.ScriptDir) | ||||
| 	} | ||||
|  | ||||
| 	if dirs.PkgDir != "" { | ||||
| 		env = append(env, "pkgdir="+dirs.PkgDir) | ||||
| 	} | ||||
|  | ||||
| 	if dirs.SrcDir != "" { | ||||
| 		env = append(env, "srcdir="+dirs.SrcDir) | ||||
| 	} | ||||
|  | ||||
| 	return env | ||||
| } | ||||
|  | ||||
| // Функция setScripts добавляет скрипты-перехватчики к метаданным пакета. | ||||
| func setScripts(vars *types.BuildVars, info *nfpm.Info, scriptDir string) { | ||||
| 	if vars.Scripts.PreInstall != "" { | ||||
| 		info.Scripts.PreInstall = filepath.Join(scriptDir, vars.Scripts.PreInstall) | ||||
| 	} | ||||
|  | ||||
| 	if vars.Scripts.PostInstall != "" { | ||||
| 		info.Scripts.PostInstall = filepath.Join(scriptDir, vars.Scripts.PostInstall) | ||||
| 	} | ||||
|  | ||||
| 	if vars.Scripts.PreRemove != "" { | ||||
| 		info.Scripts.PreRemove = filepath.Join(scriptDir, vars.Scripts.PreRemove) | ||||
| 	} | ||||
|  | ||||
| 	if vars.Scripts.PostRemove != "" { | ||||
| 		info.Scripts.PostRemove = filepath.Join(scriptDir, vars.Scripts.PostRemove) | ||||
| 	} | ||||
|  | ||||
| 	if vars.Scripts.PreUpgrade != "" { | ||||
| 		info.ArchLinux.Scripts.PreUpgrade = filepath.Join(scriptDir, vars.Scripts.PreUpgrade) | ||||
| 		info.APK.Scripts.PreUpgrade = filepath.Join(scriptDir, vars.Scripts.PreUpgrade) | ||||
| 	} | ||||
|  | ||||
| 	if vars.Scripts.PostUpgrade != "" { | ||||
| 		info.ArchLinux.Scripts.PostUpgrade = filepath.Join(scriptDir, vars.Scripts.PostUpgrade) | ||||
| 		info.APK.Scripts.PostUpgrade = filepath.Join(scriptDir, vars.Scripts.PostUpgrade) | ||||
| 	} | ||||
|  | ||||
| 	if vars.Scripts.PreTrans != "" { | ||||
| 		info.RPM.Scripts.PreTrans = filepath.Join(scriptDir, vars.Scripts.PreTrans) | ||||
| 	} | ||||
|  | ||||
| 	if vars.Scripts.PostTrans != "" { | ||||
| 		info.RPM.Scripts.PostTrans = filepath.Join(scriptDir, vars.Scripts.PostTrans) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
| // Функция setVersion изменяет переменную версии в скрипте runner. | ||||
| // Она используется для установки версии на вывод функции version(). | ||||
| func setVersion(ctx context.Context, r *interp.Runner, to string) error { | ||||
| 	fl, err := syntax.NewParser().Parse(strings.NewReader("version='"+to+"'"), "") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	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 { | ||||
| 		names[i] = p.Name | ||||
| 	} | ||||
| 	return names | ||||
| } | ||||
|  | ||||
| // Функция removeDuplicates убирает любые дубликаты из предоставленного среза. | ||||
| func removeDuplicates(slice []string) []string { | ||||
| 	seen := map[string]struct{}{} | ||||
| 	result := []string{} | ||||
|  | ||||
| 	for _, s := range slice { | ||||
| 		if _, ok := seen[s]; !ok { | ||||
| 			seen[s] = struct{}{} | ||||
| 			result = append(result, s) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return result | ||||
| } | ||||
| @@ -22,6 +22,8 @@ package repos | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log/slog" | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| @@ -41,8 +43,10 @@ import ( | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/decoder" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||
| ) | ||||
|  | ||||
| type actionType uint8 | ||||
| @@ -177,6 +181,96 @@ func (rs *Repos) Pull(ctx context.Context, repos []types.Repo) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (rs *Repos) updatePkg(ctx context.Context, repo types.Repo, runner *interp.Runner, scriptFl io.ReadCloser) error { | ||||
| 	parser := syntax.NewParser() | ||||
|  | ||||
| 	defer scriptFl.Close() | ||||
| 	fl, err := parser.Parse(scriptFl, "alr.sh") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	runner.Reset() | ||||
| 	err = runner.Run(ctx, fl) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	type packages struct { | ||||
| 		BasePkgName string   `sh:"basepkg_name"` | ||||
| 		Names       []string `sh:"name"` | ||||
| 	} | ||||
|  | ||||
| 	var pkgs packages | ||||
|  | ||||
| 	d := decoder.New(&distro.OSRelease{}, runner) | ||||
| 	d.Overrides = false | ||||
| 	d.LikeDistros = false | ||||
| 	err = d.DecodeVars(&pkgs) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if len(pkgs.Names) > 1 { | ||||
| 		if pkgs.BasePkgName == "" { | ||||
| 			pkgs.BasePkgName = pkgs.Names[0] | ||||
| 		} | ||||
| 		for _, pkgName := range pkgs.Names { | ||||
| 			pkgInfo := PackageInfo{} | ||||
| 			funcName := fmt.Sprintf("meta_%s", pkgName) | ||||
| 			runner.Reset() | ||||
| 			err = runner.Run(ctx, fl) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			meta, ok := d.GetFuncWithSubshell(funcName) | ||||
| 			if !ok { | ||||
| 				return errors.New("func is missing") | ||||
| 			} | ||||
| 			r, err := meta(ctx) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			d := decoder.New(&distro.OSRelease{}, r) | ||||
| 			d.Overrides = false | ||||
| 			d.LikeDistros = false | ||||
| 			err = d.DecodeVars(&pkgInfo) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			pkg := pkgInfo.ToPackage(repo.Name) | ||||
| 			resolveOverrides(r, pkg) | ||||
| 			pkg.Name = pkgName | ||||
| 			pkg.BasePkgName = pkgs.BasePkgName | ||||
| 			err = rs.db.InsertPackage(ctx, *pkg) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	pkg := EmptyPackage(repo.Name) | ||||
| 	err = d.DecodeVars(pkg) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	resolveOverrides(runner, pkg) | ||||
| 	return rs.db.InsertPackage(ctx, *pkg) | ||||
| } | ||||
|  | ||||
| func (rs *Repos) processRepoChangesRunner(repoDir, scriptDir string) (*interp.Runner, error) { | ||||
| 	env := append(os.Environ(), "scriptdir="+scriptDir) | ||||
| 	return interp.New( | ||||
| 		interp.Env(expand.ListEnviron(env...)), | ||||
| 		interp.ExecHandler(handlers.NopExec), | ||||
| 		interp.ReadDirHandler(handlers.RestrictedReadDir(repoDir)), | ||||
| 		interp.StatHandler(handlers.RestrictedStat(repoDir)), | ||||
| 		interp.OpenHandler(handlers.RestrictedOpen(repoDir)), | ||||
| 		interp.StdIO(handlers.NopRWC{}, handlers.NopRWC{}, handlers.NopRWC{}), | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (rs *Repos) processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, w *git.Worktree, old, new *plumbing.Reference) error { | ||||
| 	oldCommit, err := r.CommitObject(old.Hash()) | ||||
| 	if err != nil { | ||||
| @@ -235,15 +329,7 @@ func (rs *Repos) processRepoChanges(ctx context.Context, repo types.Repo, r *git | ||||
| 	parser := syntax.NewParser() | ||||
|  | ||||
| 	for _, action := range actions { | ||||
| 		env := append(os.Environ(), "scriptdir="+filepath.Dir(filepath.Join(repoDir, action.File))) | ||||
| 		runner, err := interp.New( | ||||
| 			interp.Env(expand.ListEnviron(env...)), | ||||
| 			interp.ExecHandler(handlers.NopExec), | ||||
| 			interp.ReadDirHandler(handlers.RestrictedReadDir(repoDir)), | ||||
| 			interp.StatHandler(handlers.RestrictedStat(repoDir)), | ||||
| 			interp.OpenHandler(handlers.RestrictedOpen(repoDir)), | ||||
| 			interp.StdIO(handlers.NopRWC{}, handlers.NopRWC{}, handlers.NopRWC{}), | ||||
| 		) | ||||
| 		runner, err := rs.processRepoChangesRunner(repoDir, filepath.Dir(filepath.Join(repoDir, action.File))) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| @@ -289,23 +375,7 @@ func (rs *Repos) processRepoChanges(ctx context.Context, repo types.Repo, r *git | ||||
| 				return nil | ||||
| 			} | ||||
|  | ||||
| 			pkg := db.Package{ | ||||
| 				Description:  db.NewJSON(map[string]string{}), | ||||
| 				Homepage:     db.NewJSON(map[string]string{}), | ||||
| 				Maintainer:   db.NewJSON(map[string]string{}), | ||||
| 				Depends:      db.NewJSON(map[string][]string{}), | ||||
| 				BuildDepends: db.NewJSON(map[string][]string{}), | ||||
| 				Repository:   repo.Name, | ||||
| 			} | ||||
|  | ||||
| 			err = parseScript(ctx, parser, runner, r, &pkg) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			resolveOverrides(runner, &pkg) | ||||
|  | ||||
| 			err = rs.db.InsertPackage(ctx, pkg) | ||||
| 			err = rs.updatePkg(ctx, repo, runner, r) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| @@ -322,18 +392,8 @@ func (rs *Repos) processRepoFull(ctx context.Context, repo types.Repo, repoDir s | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	parser := syntax.NewParser() | ||||
|  | ||||
| 	for _, match := range matches { | ||||
| 		env := append(os.Environ(), "scriptdir="+filepath.Dir(match)) | ||||
| 		runner, err := interp.New( | ||||
| 			interp.Env(expand.ListEnviron(env...)), | ||||
| 			interp.ExecHandler(handlers.NopExec), | ||||
| 			interp.ReadDirHandler(handlers.RestrictedReadDir(repoDir)), | ||||
| 			interp.StatHandler(handlers.RestrictedStat(repoDir)), | ||||
| 			interp.OpenHandler(handlers.RestrictedOpen(repoDir)), | ||||
| 			interp.StdIO(handlers.NopRWC{}, handlers.NopRWC{}, handlers.NopRWC{}), | ||||
| 		) | ||||
| 		runner, err := rs.processRepoChangesRunner(repoDir, filepath.Dir(match)) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| @@ -343,23 +403,7 @@ func (rs *Repos) processRepoFull(ctx context.Context, repo types.Repo, repoDir s | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		pkg := db.Package{ | ||||
| 			Description:  db.NewJSON(map[string]string{}), | ||||
| 			Homepage:     db.NewJSON(map[string]string{}), | ||||
| 			Maintainer:   db.NewJSON(map[string]string{}), | ||||
| 			Depends:      db.NewJSON(map[string][]string{}), | ||||
| 			BuildDepends: db.NewJSON(map[string][]string{}), | ||||
| 			Repository:   repo.Name, | ||||
| 		} | ||||
|  | ||||
| 		err = parseScript(ctx, parser, runner, scriptFl, &pkg) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		resolveOverrides(runner, &pkg) | ||||
|  | ||||
| 		err = rs.db.InsertPackage(ctx, pkg) | ||||
| 		err = rs.updatePkg(ctx, repo, runner, scriptFl) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|   | ||||
							
								
								
									
										173
									
								
								pkg/repos/pull_internal_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								pkg/repos/pull_internal_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,173 @@ | ||||
| // 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 repos | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||
| ) | ||||
|  | ||||
| type TestALRConfig struct{} | ||||
|  | ||||
| func (c *TestALRConfig) GetPaths(ctx context.Context) *config.Paths { | ||||
| 	return &config.Paths{ | ||||
| 		DBPath: ":memory:", | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (c *TestALRConfig) Repos(ctx context.Context) []types.Repo { | ||||
| 	return []types.Repo{ | ||||
| 		{ | ||||
| 			Name: "test", | ||||
| 			URL:  "https://test", | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func createReadCloserFromString(input string) io.ReadCloser { | ||||
| 	reader := strings.NewReader(input) | ||||
| 	return struct { | ||||
| 		io.Reader | ||||
| 		io.Closer | ||||
| 	}{ | ||||
| 		Reader: reader, | ||||
| 		Closer: io.NopCloser(reader), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestUpdatePkg(t *testing.T) { | ||||
| 	type testCase struct { | ||||
| 		name   string | ||||
| 		file   string | ||||
| 		verify func(context.Context, *db.Database) | ||||
| 	} | ||||
|  | ||||
| 	repo := types.Repo{ | ||||
| 		Name: "test", | ||||
| 		URL:  "https://test", | ||||
| 	} | ||||
|  | ||||
| 	for _, tc := range []testCase{ | ||||
| 		{ | ||||
| 			name: "single package", | ||||
| 			file: `name=foo | ||||
| version='0.0.1' | ||||
| release=1 | ||||
| desc="main desc" | ||||
| deps=('sudo') | ||||
| build_deps=('golang') | ||||
| `, | ||||
| 			verify: func(ctx context.Context, database *db.Database) { | ||||
| 				result, err := database.GetPkgs(ctx, "1 = 1") | ||||
| 				assert.NoError(t, err) | ||||
| 				pkgCount := 0 | ||||
| 				for result.Next() { | ||||
| 					var dbPkg db.Package | ||||
| 					err = result.StructScan(&dbPkg) | ||||
| 					if err != nil { | ||||
| 						t.Errorf("Expected no error, got %s", err) | ||||
| 					} | ||||
|  | ||||
| 					assert.Equal(t, "foo", dbPkg.Name) | ||||
| 					assert.Equal(t, db.NewJSON(map[string]string{"": "main desc"}), dbPkg.Description) | ||||
| 					assert.Equal(t, db.NewJSON(map[string][]string{"": {"sudo"}}), dbPkg.Depends) | ||||
| 					pkgCount++ | ||||
| 				} | ||||
| 				assert.Equal(t, 1, pkgCount) | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "multiple package", | ||||
| 			file: `basepkg_name=foo | ||||
| name=( | ||||
| 	bar | ||||
| 	buz | ||||
| ) | ||||
| version='0.0.1' | ||||
| release=1 | ||||
| desc="main desc" | ||||
| deps=('sudo') | ||||
| build_deps=('golang') | ||||
| 		 | ||||
| meta_bar() { | ||||
| 	desc="foo desc" | ||||
| } | ||||
| 			 | ||||
| meta_buz() { | ||||
| 	deps+=('doas') | ||||
| } | ||||
| `, | ||||
| 			verify: func(ctx context.Context, database *db.Database) { | ||||
| 				result, err := database.GetPkgs(ctx, "1 = 1") | ||||
| 				assert.NoError(t, err) | ||||
|  | ||||
| 				pkgCount := 0 | ||||
| 				for result.Next() { | ||||
| 					var dbPkg db.Package | ||||
| 					err = result.StructScan(&dbPkg) | ||||
| 					if err != nil { | ||||
| 						t.Errorf("Expected no error, got %s", err) | ||||
| 					} | ||||
| 					if dbPkg.Name == "bar" { | ||||
| 						assert.Equal(t, db.NewJSON(map[string]string{"": "foo desc"}), dbPkg.Description) | ||||
| 						assert.Equal(t, db.NewJSON(map[string][]string{"": {"sudo"}}), dbPkg.Depends) | ||||
| 					} | ||||
|  | ||||
| 					if dbPkg.Name == "buz" { | ||||
| 						assert.Equal(t, db.NewJSON(map[string]string{"": "main desc"}), dbPkg.Description) | ||||
| 						assert.Equal(t, db.NewJSON(map[string][]string{"": {"sudo", "doas"}}), dbPkg.Depends) | ||||
| 					} | ||||
| 					pkgCount++ | ||||
| 				} | ||||
| 				assert.Equal(t, 2, pkgCount) | ||||
| 			}, | ||||
| 		}, | ||||
| 	} { | ||||
| 		t.Run(tc.name, func(t *testing.T) { | ||||
| 			cfg := &TestALRConfig{} | ||||
| 			ctx := context.Background() | ||||
|  | ||||
| 			database := db.New(&TestALRConfig{}) | ||||
| 			database.Init(ctx) | ||||
|  | ||||
| 			rs := New(cfg, database) | ||||
|  | ||||
| 			path, err := os.MkdirTemp("", "test-update-pkg") | ||||
| 			assert.NoError(t, err) | ||||
| 			defer os.RemoveAll(path) | ||||
|  | ||||
| 			runner, err := rs.processRepoChangesRunner(path, path) | ||||
| 			assert.NoError(t, err) | ||||
|  | ||||
| 			err = rs.updatePkg(ctx, repo, runner, createReadCloserFromString( | ||||
| 				tc.file, | ||||
| 			)) | ||||
| 			assert.NoError(t, err) | ||||
|  | ||||
| 			tc.verify(ctx, database) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| @@ -21,7 +21,6 @@ import ( | ||||
| 	"sync" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||
| 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||
| ) | ||||
| @@ -36,15 +35,6 @@ func Pull(ctx context.Context, repos []types.Repo) error { | ||||
| 	return GetInstance(ctx).Pull(ctx, repos) | ||||
| } | ||||
|  | ||||
| // FindPkgs looks for packages matching the inputs inside the database. | ||||
| // It returns a map that maps the package name input to any packages found for it. | ||||
| // It also returns a slice that contains the names of all packages that were not found. | ||||
| // | ||||
| // Deprecated: use struct method | ||||
| func FindPkgs(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error) { | ||||
| 	return GetInstance(ctx).FindPkgs(ctx, pkgs) | ||||
| } | ||||
|  | ||||
| // ======================= | ||||
| // FOR LEGACY ONLY | ||||
| // ======================= | ||||
|   | ||||
| @@ -67,6 +67,47 @@ func parseScript(ctx context.Context, parser *syntax.Parser, runner *interp.Runn | ||||
| 	return d.DecodeVars(pkg) | ||||
| } | ||||
|  | ||||
| type PackageInfo struct { | ||||
| 	Version       string            `sh:"version,required"` | ||||
| 	Release       int               `sh:"release,required"` | ||||
| 	Epoch         uint              `sh:"epoch"` | ||||
| 	Architectures db.JSON[[]string] `sh:"architectures"` | ||||
| 	Licenses      db.JSON[[]string] `sh:"license"` | ||||
| 	Provides      db.JSON[[]string] `sh:"provides"` | ||||
| 	Conflicts     db.JSON[[]string] `sh:"conflicts"` | ||||
| 	Replaces      db.JSON[[]string] `sh:"replaces"` | ||||
| } | ||||
|  | ||||
| func (inf *PackageInfo) ToPackage(repoName string) *db.Package { | ||||
| 	return &db.Package{ | ||||
| 		Version:       inf.Version, | ||||
| 		Release:       inf.Release, | ||||
| 		Epoch:         inf.Epoch, | ||||
| 		Architectures: inf.Architectures, | ||||
| 		Licenses:      inf.Licenses, | ||||
| 		Provides:      inf.Provides, | ||||
| 		Conflicts:     inf.Conflicts, | ||||
| 		Replaces:      inf.Replaces, | ||||
| 		Description:   db.NewJSON(map[string]string{}), | ||||
| 		Homepage:      db.NewJSON(map[string]string{}), | ||||
| 		Maintainer:    db.NewJSON(map[string]string{}), | ||||
| 		Depends:       db.NewJSON(map[string][]string{}), | ||||
| 		BuildDepends:  db.NewJSON(map[string][]string{}), | ||||
| 		Repository:    repoName, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func EmptyPackage(repoName string) *db.Package { | ||||
| 	return &db.Package{ | ||||
| 		Description:  db.NewJSON(map[string]string{}), | ||||
| 		Homepage:     db.NewJSON(map[string]string{}), | ||||
| 		Maintainer:   db.NewJSON(map[string]string{}), | ||||
| 		Depends:      db.NewJSON(map[string][]string{}), | ||||
| 		BuildDepends: db.NewJSON(map[string][]string{}), | ||||
| 		Repository:   repoName, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var overridable = map[string]string{ | ||||
| 	"deps":       "Depends", | ||||
| 	"build_deps": "BuildDepends", | ||||
|   | ||||
| @@ -1,3 +1,22 @@ | ||||
| # This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||
| # It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов. | ||||
| # | ||||
| # 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/>. | ||||
|  | ||||
| info() { | ||||
|   echo $'\x1b[32m[ИНФО]\x1b[0m' $@ | ||||
| } | ||||
|   | ||||
							
								
								
									
										44
									
								
								upgrade.go
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								upgrade.go
									
									
									
									
									
								
							| @@ -32,7 +32,7 @@ import ( | ||||
| 	"golang.org/x/exp/slices" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||
| 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | ||||
| @@ -56,7 +56,14 @@ func UpgradeCmd() *cli.Command { | ||||
| 		Action: func(c *cli.Context) error { | ||||
| 			ctx := c.Context | ||||
|  | ||||
| 			cfg := config.GetInstance(ctx) | ||||
| 			cfg := config.New() | ||||
| 			db := database.New(cfg) | ||||
| 			rs := repos.New(cfg, db) | ||||
| 			err := db.Init(ctx) | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error db init"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			info, err := distro.ParseOSRelease(ctx) | ||||
| 			if err != nil { | ||||
| @@ -71,21 +78,32 @@ func UpgradeCmd() *cli.Command { | ||||
| 			} | ||||
|  | ||||
| 			if cfg.AutoPull(ctx) { | ||||
| 				err = repos.Pull(ctx, config.Config(ctx).Repos) | ||||
| 				err = rs.Pull(ctx, cfg.Repos(ctx)) | ||||
| 				if err != nil { | ||||
| 					slog.Error(gotext.Get("Error pulling repos"), "err", err) | ||||
| 					os.Exit(1) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			updates, err := checkForUpdates(ctx, mgr, info) | ||||
| 			updates, err := checkForUpdates(ctx, mgr, cfg, rs, info) | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error checking for updates"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			if len(updates) > 0 { | ||||
| 				build.InstallPkgs(ctx, updates, nil, types.BuildOpts{ | ||||
| 				builder := build.NewBuilder( | ||||
| 					ctx, | ||||
| 					types.BuildOpts{ | ||||
| 						Manager:     mgr, | ||||
| 						Clean:       c.Bool("clean"), | ||||
| 						Interactive: c.Bool("interactive"), | ||||
| 					}, | ||||
| 					rs, | ||||
| 					info, | ||||
| 					cfg, | ||||
| 				) | ||||
| 				builder.InstallPkgs(ctx, updates, nil, types.BuildOpts{ | ||||
| 					Manager:     mgr, | ||||
| 					Clean:       c.Bool("clean"), | ||||
| 					Interactive: c.Bool("interactive"), | ||||
| @@ -99,27 +117,33 @@ func UpgradeCmd() *cli.Command { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func checkForUpdates(ctx context.Context, mgr manager.Manager, info *distro.OSRelease) ([]db.Package, error) { | ||||
| func checkForUpdates( | ||||
| 	ctx context.Context, | ||||
| 	mgr manager.Manager, | ||||
| 	cfg *config.ALRConfig, | ||||
| 	rs *repos.Repos, | ||||
| 	info *distro.OSRelease, | ||||
| ) ([]database.Package, error) { | ||||
| 	installed, err := mgr.ListInstalled(nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	pkgNames := maps.Keys(installed) | ||||
| 	found, _, err := repos.FindPkgs(ctx, pkgNames) | ||||
| 	found, _, err := rs.FindPkgs(ctx, pkgNames) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var out []db.Package | ||||
| 	var out []database.Package | ||||
| 	for pkgName, pkgs := range found { | ||||
| 		if slices.Contains(config.Config(ctx).IgnorePkgUpdates, pkgName) { | ||||
| 		if slices.Contains(cfg.IgnorePkgUpdates(ctx), pkgName) { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		if len(pkgs) > 1 { | ||||
| 			// Puts the element with the highest version first | ||||
| 			slices.SortFunc(pkgs, func(a, b db.Package) int { | ||||
| 			slices.SortFunc(pkgs, func(a, b database.Package) int { | ||||
| 				return vercmp.Compare(a.Version, b.Version) | ||||
| 			}) | ||||
| 		} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user