forked from Plemya-x/ALR
		
	refactor: migrate repo to struct
This commit is contained in:
		| @@ -55,6 +55,7 @@ var ( | |||||||
| 	alrConfigOnce sync.Once | 	alrConfigOnce sync.Once | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // Deprecated: For legacy only | ||||||
| func GetInstance(ctx context.Context) *ALRConfig { | func GetInstance(ctx context.Context) *ALRConfig { | ||||||
| 	alrConfigOnce.Do(func() { | 	alrConfigOnce.Do(func() { | ||||||
| 		alrConfig = New() | 		alrConfig = New() | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ import ( | |||||||
| // | // | ||||||
| // Deprecated: use struct method | // Deprecated: use struct method | ||||||
| func DB(ctx context.Context) *sqlx.DB { | func DB(ctx context.Context) *sqlx.DB { | ||||||
| 	return getInstance(ctx).GetConn() | 	return GetInstance(ctx).GetConn() | ||||||
| } | } | ||||||
|  |  | ||||||
| // Close closes the database | // Close closes the database | ||||||
| @@ -50,35 +50,35 @@ func Close() error { | |||||||
| // | // | ||||||
| // Deprecated: use struct method | // Deprecated: use struct method | ||||||
| func IsEmpty(ctx context.Context) bool { | func IsEmpty(ctx context.Context) bool { | ||||||
| 	return getInstance(ctx).IsEmpty(ctx) | 	return GetInstance(ctx).IsEmpty(ctx) | ||||||
| } | } | ||||||
|  |  | ||||||
| // InsertPackage adds a package to the database | // InsertPackage adds a package to the database | ||||||
| // | // | ||||||
| // Deprecated: use struct method | // Deprecated: use struct method | ||||||
| func InsertPackage(ctx context.Context, pkg Package) error { | func InsertPackage(ctx context.Context, pkg Package) error { | ||||||
| 	return getInstance(ctx).InsertPackage(ctx, pkg) | 	return GetInstance(ctx).InsertPackage(ctx, pkg) | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetPkgs returns a result containing packages that match the where conditions | // GetPkgs returns a result containing packages that match the where conditions | ||||||
| // | // | ||||||
| // Deprecated: use struct method | // Deprecated: use struct method | ||||||
| func GetPkgs(ctx context.Context, where string, args ...any) (*sqlx.Rows, error) { | func GetPkgs(ctx context.Context, where string, args ...any) (*sqlx.Rows, error) { | ||||||
| 	return getInstance(ctx).GetPkgs(ctx, where, args...) | 	return GetInstance(ctx).GetPkgs(ctx, where, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetPkg returns a single package that matches the where conditions | // GetPkg returns a single package that matches the where conditions | ||||||
| // | // | ||||||
| // Deprecated: use struct method | // Deprecated: use struct method | ||||||
| func GetPkg(ctx context.Context, where string, args ...any) (*Package, error) { | func GetPkg(ctx context.Context, where string, args ...any) (*Package, error) { | ||||||
| 	return getInstance(ctx).GetPkg(ctx, where, args...) | 	return GetInstance(ctx).GetPkg(ctx, where, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // DeletePkgs deletes all packages matching the where conditions | // DeletePkgs deletes all packages matching the where conditions | ||||||
| // | // | ||||||
| // Deprecated: use struct method | // Deprecated: use struct method | ||||||
| func DeletePkgs(ctx context.Context, where string, args ...any) error { | func DeletePkgs(ctx context.Context, where string, args ...any) error { | ||||||
| 	return getInstance(ctx).DeletePkgs(ctx, where, args...) | 	return GetInstance(ctx).DeletePkgs(ctx, where, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // ======================= | // ======================= | ||||||
| @@ -90,8 +90,8 @@ var ( | |||||||
| 	database *Database | 	database *Database | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // For refactoring only | // Deprecated: For legacy only | ||||||
| func getInstance(ctx context.Context) *Database { | func GetInstance(ctx context.Context) *Database { | ||||||
| 	dbOnce.Do(func() { | 	dbOnce.Do(func() { | ||||||
| 		log := loggerctx.From(ctx) | 		log := loggerctx.From(ctx) | ||||||
| 		cfg := config.GetInstance(ctx) | 		cfg := config.GetInstance(ctx) | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								list.go
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								list.go
									
									
									
									
									
								
							| @@ -44,14 +44,13 @@ var listCmd = &cli.Command{ | |||||||
| 		ctx := c.Context | 		ctx := c.Context | ||||||
| 		log := loggerctx.From(ctx) | 		log := loggerctx.From(ctx) | ||||||
| 		cfg := config.New() | 		cfg := config.New() | ||||||
|  |  | ||||||
| 		db := database.New(cfg) | 		db := database.New(cfg) | ||||||
| 		err := db.Init(ctx) | 		err := db.Init(ctx) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal("Error initialization database").Err(err).Send() | 			log.Fatal("Error initialization database").Err(err).Send() | ||||||
| 		} | 		} | ||||||
|  | 		rs := repos.New(cfg, db) | ||||||
| 		err = repos.Pull(ctx, cfg.Repos(ctx)) | 		err = rs.Pull(ctx, cfg.Repos(ctx)) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal("Error pulling repositories").Err(err).Send() | 			log.Fatal("Error pulling repositories").Err(err).Send() | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ | |||||||
|  |  | ||||||
| package repos_test | package repos_test | ||||||
|  |  | ||||||
|  | /* | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| @@ -146,3 +147,4 @@ func TestFindPkgsEmpty(t *testing.T) { | |||||||
| 		t.Errorf("Expected 'test2' package, got '%s'", testPkgs[0].Name) | 		t.Errorf("Expected 'test2' package, got '%s'", testPkgs[0].Name) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | */ | ||||||
|   | |||||||
| @@ -21,41 +21,48 @@ package repos | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"io" |  | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"reflect" |  | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"github.com/go-git/go-billy/v5" | 	"github.com/go-git/go-billy/v5" | ||||||
| 	"github.com/go-git/go-billy/v5/osfs" | 	"github.com/go-git/go-billy/v5/osfs" | ||||||
| 	"github.com/go-git/go-git/v5" | 	"github.com/go-git/go-git/v5" | ||||||
| 	"github.com/go-git/go-git/v5/plumbing" | 	"github.com/go-git/go-git/v5/plumbing" | ||||||
| 	"github.com/go-git/go-git/v5/plumbing/format/diff" |  | ||||||
| 	"github.com/pelletier/go-toml/v2" | 	"github.com/pelletier/go-toml/v2" | ||||||
| 	"go.elara.ws/vercmp" | 	"go.elara.ws/vercmp" | ||||||
| 	"plemya-x.ru/alr/internal/config" |  | ||||||
| 	"plemya-x.ru/alr/internal/db" |  | ||||||
| 	"plemya-x.ru/alr/internal/shutils/decoder" |  | ||||||
| 	"plemya-x.ru/alr/internal/shutils/handlers" |  | ||||||
| 	"plemya-x.ru/alr/internal/types" |  | ||||||
| 	"plemya-x.ru/alr/pkg/distro" |  | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" |  | ||||||
| 	"mvdan.cc/sh/v3/expand" | 	"mvdan.cc/sh/v3/expand" | ||||||
| 	"mvdan.cc/sh/v3/interp" | 	"mvdan.cc/sh/v3/interp" | ||||||
| 	"mvdan.cc/sh/v3/syntax" | 	"mvdan.cc/sh/v3/syntax" | ||||||
|  | 	"plemya-x.ru/alr/internal/config" | ||||||
|  | 	"plemya-x.ru/alr/internal/db" | ||||||
|  | 	"plemya-x.ru/alr/internal/shutils/handlers" | ||||||
|  | 	"plemya-x.ru/alr/internal/types" | ||||||
|  | 	"plemya-x.ru/alr/pkg/loggerctx" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | type actionType uint8 | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	actionDelete actionType = iota | ||||||
|  | 	actionUpdate | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type action struct { | ||||||
|  | 	Type actionType | ||||||
|  | 	File string | ||||||
|  | } | ||||||
|  |  | ||||||
| // Pull pulls the provided repositories. If a repo doesn't exist, it will be cloned | // Pull pulls the provided repositories. If a repo doesn't exist, it will be cloned | ||||||
| // and its packages will be written to the DB. If it does exist, it will be pulled. | // and its packages will be written to the DB. If it does exist, it will be pulled. | ||||||
| // In this case, only changed packages will be processed if possible. | // In this case, only changed packages will be processed if possible. | ||||||
| // If repos is set to nil, the repos in the ALR config will be used. | // If repos is set to nil, the repos in the ALR config will be used. | ||||||
| func Pull(ctx context.Context, repos []types.Repo) error { | func (rs *Repos) Pull(ctx context.Context, repos []types.Repo) error { | ||||||
| 	log := loggerctx.From(ctx) | 	log := loggerctx.From(ctx) | ||||||
|  |  | ||||||
| 	if repos == nil { | 	if repos == nil { | ||||||
| 		repos = config.Config(ctx).Repos | 		repos = rs.cfg.Repos(ctx) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, repo := range repos { | 	for _, repo := range repos { | ||||||
| @@ -95,7 +102,7 @@ func Pull(ctx context.Context, repos []types.Repo) error { | |||||||
| 			repoFS = w.Filesystem | 			repoFS = w.Filesystem | ||||||
|  |  | ||||||
| 			// Make sure the DB is created even if the repo is up to date | 			// Make sure the DB is created even if the repo is up to date | ||||||
| 			if !errors.Is(err, git.NoErrAlreadyUpToDate) || db.IsEmpty(ctx) { | 			if !errors.Is(err, git.NoErrAlreadyUpToDate) || rs.db.IsEmpty(ctx) { | ||||||
| 				new, err := r.Head() | 				new, err := r.Head() | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					return err | 					return err | ||||||
| @@ -104,13 +111,13 @@ func Pull(ctx context.Context, repos []types.Repo) error { | |||||||
| 				// If the DB was not present at startup, that means it's | 				// If the DB was not present at startup, that means it's | ||||||
| 				// empty. In this case, we need to update the DB fully | 				// empty. In this case, we need to update the DB fully | ||||||
| 				// rather than just incrementally. | 				// rather than just incrementally. | ||||||
| 				if db.IsEmpty(ctx) { | 				if rs.db.IsEmpty(ctx) { | ||||||
| 					err = processRepoFull(ctx, repo, repoDir) | 					err = rs.processRepoFull(ctx, repo, repoDir) | ||||||
| 					if err != nil { | 					if err != nil { | ||||||
| 						return err | 						return err | ||||||
| 					} | 					} | ||||||
| 				} else { | 				} else { | ||||||
| 					err = processRepoChanges(ctx, repo, r, w, old, new) | 					err = rs.processRepoChanges(ctx, repo, r, w, old, new) | ||||||
| 					if err != nil { | 					if err != nil { | ||||||
| 						return err | 						return err | ||||||
| 					} | 					} | ||||||
| @@ -135,7 +142,7 @@ func Pull(ctx context.Context, repos []types.Repo) error { | |||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			err = processRepoFull(ctx, repo, repoDir) | 			err = rs.processRepoFull(ctx, repo, repoDir) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| @@ -169,19 +176,7 @@ func Pull(ctx context.Context, repos []types.Repo) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| type actionType uint8 | func (rs *Repos) processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, w *git.Worktree, old, new *plumbing.Reference) error { | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	actionDelete actionType = iota |  | ||||||
| 	actionUpdate |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type action struct { |  | ||||||
| 	Type actionType |  | ||||||
| 	File string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, w *git.Worktree, old, new *plumbing.Reference) error { |  | ||||||
| 	oldCommit, err := r.CommitObject(old.Hash()) | 	oldCommit, err := r.CommitObject(old.Hash()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -275,7 +270,7 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, | |||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			err = db.DeletePkgs(ctx, "name = ? AND repository = ?", pkg.Name, repo.Name) | 			err = rs.db.DeletePkgs(ctx, "name = ? AND repository = ?", pkg.Name, repo.Name) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| @@ -310,7 +305,7 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, | |||||||
|  |  | ||||||
| 			resolveOverrides(runner, &pkg) | 			resolveOverrides(runner, &pkg) | ||||||
|  |  | ||||||
| 			err = db.InsertPackage(ctx, pkg) | 			err = rs.db.InsertPackage(ctx, pkg) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| @@ -320,23 +315,7 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // isValid makes sure the path of the file being updated is valid. | func (rs *Repos) processRepoFull(ctx context.Context, repo types.Repo, repoDir string) error { | ||||||
| // It checks to make sure the file is not within a nested directory |  | ||||||
| // and that it is called alr.sh. |  | ||||||
| func isValid(from, to diff.File) bool { |  | ||||||
| 	var path string |  | ||||||
| 	if from != nil { |  | ||||||
| 		path = from.Path() |  | ||||||
| 	} |  | ||||||
| 	if to != nil { |  | ||||||
| 		path = to.Path() |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	match, _ := filepath.Match("*/*.sh", path) |  | ||||||
| 	return match |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func processRepoFull(ctx context.Context, repo types.Repo, repoDir string) error { |  | ||||||
| 	glob := filepath.Join(repoDir, "/*/alr.sh") | 	glob := filepath.Join(repoDir, "/*/alr.sh") | ||||||
| 	matches, err := filepath.Glob(glob) | 	matches, err := filepath.Glob(glob) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -380,7 +359,7 @@ func processRepoFull(ctx context.Context, repo types.Repo, repoDir string) error | |||||||
|  |  | ||||||
| 		resolveOverrides(runner, &pkg) | 		resolveOverrides(runner, &pkg) | ||||||
|  |  | ||||||
| 		err = db.InsertPackage(ctx, pkg) | 		err = rs.db.InsertPackage(ctx, pkg) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @@ -388,54 +367,3 @@ func processRepoFull(ctx context.Context, repo types.Repo, repoDir string) error | |||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func parseScript(ctx context.Context, parser *syntax.Parser, runner *interp.Runner, r io.ReadCloser, pkg *db.Package) error { |  | ||||||
| 	defer r.Close() |  | ||||||
| 	fl, err := parser.Parse(r, "alr.sh") |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	runner.Reset() |  | ||||||
| 	err = runner.Run(ctx, fl) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	d := decoder.New(&distro.OSRelease{}, runner) |  | ||||||
| 	d.Overrides = false |  | ||||||
| 	d.LikeDistros = false |  | ||||||
| 	return d.DecodeVars(pkg) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var overridable = map[string]string{ |  | ||||||
| 	"deps":       "Depends", |  | ||||||
| 	"build_deps": "BuildDepends", |  | ||||||
| 	"desc":       "Description", |  | ||||||
| 	"homepage":   "Homepage", |  | ||||||
| 	"maintainer": "Maintainer", |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func resolveOverrides(runner *interp.Runner, pkg *db.Package) { |  | ||||||
| 	pkgVal := reflect.ValueOf(pkg).Elem() |  | ||||||
| 	for name, val := range runner.Vars { |  | ||||||
| 		for prefix, field := range overridable { |  | ||||||
| 			if strings.HasPrefix(name, prefix) { |  | ||||||
| 				override := strings.TrimPrefix(name, prefix) |  | ||||||
| 				override = strings.TrimPrefix(override, "_") |  | ||||||
|  |  | ||||||
| 				field := pkgVal.FieldByName(field) |  | ||||||
| 				varVal := field.FieldByName("Val") |  | ||||||
| 				varType := varVal.Type() |  | ||||||
|  |  | ||||||
| 				switch varType.Elem().String() { |  | ||||||
| 				case "[]string": |  | ||||||
| 					varVal.SetMapIndex(reflect.ValueOf(override), reflect.ValueOf(val.List)) |  | ||||||
| 				case "string": |  | ||||||
| 					varVal.SetMapIndex(reflect.ValueOf(override), reflect.ValueOf(val.Str)) |  | ||||||
| 				} |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|   | |||||||
							
								
								
									
										44
									
								
								pkg/repos/pull_legacy.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								pkg/repos/pull_legacy.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | package repos | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"sync" | ||||||
|  |  | ||||||
|  | 	"plemya-x.ru/alr/internal/config" | ||||||
|  | 	database "plemya-x.ru/alr/internal/db" | ||||||
|  | 	"plemya-x.ru/alr/internal/types" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Pull pulls the provided repositories. If a repo doesn't exist, it will be cloned | ||||||
|  | // and its packages will be written to the DB. If it does exist, it will be pulled. | ||||||
|  | // In this case, only changed packages will be processed if possible. | ||||||
|  | // If repos is set to nil, the repos in the ALR config will be used. | ||||||
|  | // | ||||||
|  | // Deprecated: use struct method | ||||||
|  | func Pull(ctx context.Context, repos []types.Repo) error { | ||||||
|  | 	return GetInstance(ctx).Pull(ctx, repos) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ======================= | ||||||
|  | // FOR LEGACY ONLY | ||||||
|  | // ======================= | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	reposInstance *Repos | ||||||
|  | 	alrConfigOnce sync.Once | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Deprecated: For legacy only | ||||||
|  | func GetInstance(ctx context.Context) *Repos { | ||||||
|  | 	alrConfigOnce.Do(func() { | ||||||
|  | 		cfg := config.GetInstance(ctx) | ||||||
|  | 		db := database.GetInstance(ctx) | ||||||
|  |  | ||||||
|  | 		reposInstance = New( | ||||||
|  | 			cfg, | ||||||
|  | 			db, | ||||||
|  | 		) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	return reposInstance | ||||||
|  | } | ||||||
| @@ -26,69 +26,104 @@ import ( | |||||||
|  |  | ||||||
| 	"plemya-x.ru/alr/internal/config" | 	"plemya-x.ru/alr/internal/config" | ||||||
| 	"plemya-x.ru/alr/internal/db" | 	"plemya-x.ru/alr/internal/db" | ||||||
|  | 	database "plemya-x.ru/alr/internal/db" | ||||||
| 	"plemya-x.ru/alr/internal/types" | 	"plemya-x.ru/alr/internal/types" | ||||||
| 	"plemya-x.ru/alr/pkg/repos" | 	"plemya-x.ru/alr/pkg/repos" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func setCfgDirs(t *testing.T) { | type TestEnv struct { | ||||||
| 	t.Helper() | 	Ctx context.Context | ||||||
|  | 	Cfg *TestALRConfig | ||||||
| 	paths := config.GetPaths() | 	Db  *db.Database | ||||||
|  |  | ||||||
| 	var err error |  | ||||||
| 	paths.CacheDir, err = os.MkdirTemp("/tmp", "alr-pull-test.*") |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("Expected no error, got %s", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	paths.RepoDir = filepath.Join(paths.CacheDir, "repo") |  | ||||||
| 	paths.PkgsDir = filepath.Join(paths.CacheDir, "pkgs") |  | ||||||
|  |  | ||||||
| 	err = os.MkdirAll(paths.RepoDir, 0o755) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("Expected no error, got %s", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	err = os.MkdirAll(paths.PkgsDir, 0o755) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("Expected no error, got %s", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	paths.DBPath = filepath.Join(paths.CacheDir, "db") |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func removeCacheDir(t *testing.T) { | type TestALRConfig struct { | ||||||
| 	t.Helper() | 	CacheDir string | ||||||
|  | 	RepoDir  string | ||||||
|  | 	PkgsDir  string | ||||||
|  | } | ||||||
|  |  | ||||||
| 	err := os.RemoveAll(config.GetPaths().CacheDir) | func (c *TestALRConfig) GetPaths(ctx context.Context) *config.Paths { | ||||||
| 	if err != nil { | 	return &config.Paths{ | ||||||
| 		t.Fatalf("Expected no error, got %s", err) | 		DBPath:   ":memory:", | ||||||
|  | 		CacheDir: c.CacheDir, | ||||||
|  | 		RepoDir:  c.RepoDir, | ||||||
|  | 		PkgsDir:  c.PkgsDir, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestPull(t *testing.T) { | func (c *TestALRConfig) Repos(ctx context.Context) []types.Repo { | ||||||
| 	_, err := db.Open(":memory:") | 	return []types.Repo{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func prepare(t *testing.T) *TestEnv { | ||||||
|  | 	t.Helper() | ||||||
|  |  | ||||||
|  | 	cacheDir, err := os.MkdirTemp("/tmp", "alr-pull-test.*") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("Expected no error, got %s", err) | 		t.Fatalf("Expected no error, got %s", err) | ||||||
| 	} | 	} | ||||||
| 	defer db.Close() |  | ||||||
|  |  | ||||||
| 	setCfgDirs(t) | 	repoDir := filepath.Join(cacheDir, "repo") | ||||||
| 	defer removeCacheDir(t) | 	err = os.MkdirAll(repoDir, 0o755) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("Expected no error, got %s", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pkgsDir := filepath.Join(cacheDir, "pkgs") | ||||||
|  | 	err = os.MkdirAll(pkgsDir, 0o755) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("Expected no error, got %s", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cfg := &TestALRConfig{ | ||||||
|  | 		CacheDir: cacheDir, | ||||||
|  | 		RepoDir:  repoDir, | ||||||
|  | 		PkgsDir:  pkgsDir, | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	ctx := context.Background() | 	ctx := context.Background() | ||||||
|  |  | ||||||
| 	err = repos.Pull(ctx, []types.Repo{ | 	db := database.New(cfg) | ||||||
|  | 	db.Init(ctx) | ||||||
|  |  | ||||||
|  | 	return &TestEnv{ | ||||||
|  | 		Cfg: cfg, | ||||||
|  | 		Db:  db, | ||||||
|  | 		Ctx: ctx, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func cleanup(t *testing.T, e *TestEnv) { | ||||||
|  | 	t.Helper() | ||||||
|  |  | ||||||
|  | 	err := os.RemoveAll(e.Cfg.CacheDir) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("Expected no error, got %s", err) | ||||||
|  | 	} | ||||||
|  | 	e.Db.Close() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestPull(t *testing.T) { | ||||||
|  | 	e := prepare(t) | ||||||
|  | 	defer cleanup(t, e) | ||||||
|  |  | ||||||
|  | 	rs := repos.New( | ||||||
|  | 		e.Cfg, | ||||||
|  | 		e.Db, | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	err := rs.Pull(e.Ctx, []types.Repo{ | ||||||
| 		{ | 		{ | ||||||
| 			Name: "default", | 			Name: "default", | ||||||
| 			URL:  "https://gitea.plemya-x.ru/xpamych/ALR.git", | 			URL:  "https://gitea.plemya-x.ru/Plemya-x/xpamych-alr-repo.git", | ||||||
| 		}, | 		}, | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("Expected no error, got %s", err) | 		t.Fatalf("Expected no error, got %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	result, err := db.GetPkgs("name LIKE 'itd%'") | 	result, err := e.Db.GetPkgs(e.Ctx, "true") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("Expected no error, got %s", err) | 		t.Fatalf("Expected no error, got %s", err) | ||||||
| 	} | 	} | ||||||
| @@ -103,7 +138,7 @@ func TestPull(t *testing.T) { | |||||||
| 		pkgAmt++ | 		pkgAmt++ | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if pkgAmt < 2 { | 	if pkgAmt == 0 { | ||||||
| 		t.Errorf("Expected 2 packages to match, got %d", pkgAmt) | 		t.Errorf("Expected at least 1 matching package, but got %d", pkgAmt) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								pkg/repos/repos.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								pkg/repos/repos.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | package repos | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  |  | ||||||
|  | 	"plemya-x.ru/alr/internal/config" | ||||||
|  | 	database "plemya-x.ru/alr/internal/db" | ||||||
|  | 	"plemya-x.ru/alr/internal/types" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Config interface { | ||||||
|  | 	GetPaths(ctx context.Context) *config.Paths | ||||||
|  | 	Repos(ctx context.Context) []types.Repo | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Repos struct { | ||||||
|  | 	cfg Config | ||||||
|  | 	db  *database.Database | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func New( | ||||||
|  | 	cfg Config, | ||||||
|  | 	db *database.Database, | ||||||
|  | ) *Repos { | ||||||
|  | 	return &Repos{ | ||||||
|  | 		cfg, | ||||||
|  | 		db, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										83
									
								
								pkg/repos/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								pkg/repos/utils.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | package repos | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"io" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"reflect" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"github.com/go-git/go-git/v5/plumbing/format/diff" | ||||||
|  | 	"mvdan.cc/sh/v3/interp" | ||||||
|  | 	"mvdan.cc/sh/v3/syntax" | ||||||
|  | 	"plemya-x.ru/alr/internal/db" | ||||||
|  | 	"plemya-x.ru/alr/internal/shutils/decoder" | ||||||
|  | 	"plemya-x.ru/alr/pkg/distro" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // isValid makes sure the path of the file being updated is valid. | ||||||
|  | // It checks to make sure the file is not within a nested directory | ||||||
|  | // and that it is called alr.sh. | ||||||
|  | func isValid(from, to diff.File) bool { | ||||||
|  | 	var path string | ||||||
|  | 	if from != nil { | ||||||
|  | 		path = from.Path() | ||||||
|  | 	} | ||||||
|  | 	if to != nil { | ||||||
|  | 		path = to.Path() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	match, _ := filepath.Match("*/*.sh", path) | ||||||
|  | 	return match | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func parseScript(ctx context.Context, parser *syntax.Parser, runner *interp.Runner, r io.ReadCloser, pkg *db.Package) error { | ||||||
|  | 	defer r.Close() | ||||||
|  | 	fl, err := parser.Parse(r, "alr.sh") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	runner.Reset() | ||||||
|  | 	err = runner.Run(ctx, fl) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	d := decoder.New(&distro.OSRelease{}, runner) | ||||||
|  | 	d.Overrides = false | ||||||
|  | 	d.LikeDistros = false | ||||||
|  | 	return d.DecodeVars(pkg) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var overridable = map[string]string{ | ||||||
|  | 	"deps":       "Depends", | ||||||
|  | 	"build_deps": "BuildDepends", | ||||||
|  | 	"desc":       "Description", | ||||||
|  | 	"homepage":   "Homepage", | ||||||
|  | 	"maintainer": "Maintainer", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func resolveOverrides(runner *interp.Runner, pkg *db.Package) { | ||||||
|  | 	pkgVal := reflect.ValueOf(pkg).Elem() | ||||||
|  | 	for name, val := range runner.Vars { | ||||||
|  | 		for prefix, field := range overridable { | ||||||
|  | 			if strings.HasPrefix(name, prefix) { | ||||||
|  | 				override := strings.TrimPrefix(name, prefix) | ||||||
|  | 				override = strings.TrimPrefix(override, "_") | ||||||
|  |  | ||||||
|  | 				field := pkgVal.FieldByName(field) | ||||||
|  | 				varVal := field.FieldByName("Val") | ||||||
|  | 				varType := varVal.Type() | ||||||
|  |  | ||||||
|  | 				switch varType.Elem().String() { | ||||||
|  | 				case "[]string": | ||||||
|  | 					varVal.SetMapIndex(reflect.ValueOf(override), reflect.ValueOf(val.List)) | ||||||
|  | 				case "string": | ||||||
|  | 					varVal.SetMapIndex(reflect.ValueOf(override), reflect.ValueOf(val.Str)) | ||||||
|  | 				} | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user