From 6ad63cae10abebb4699b694c1d70298c4b7153ec Mon Sep 17 00:00:00 2001 From: Jo Date: Sun, 20 Nov 2022 00:51:55 +0000 Subject: [PATCH] fix: rework menus to work on both flows (#1830) * rework menus to work on both flows * add installed package split * remove unused field * Add post install hooks --- clean.go | 9 ++-- install.go | 53 ++++++++++---------- pkg/db/executor.go | 3 ++ pkg/db/ialpm/alpm.go | 19 ++++++-- pkg/db/ialpm/high_level.go | 46 ++++++++++++++++++ pkg/db/mock/executor.go | 4 +- pkg/menus/clean_menu.go | 79 ++++++++++++++++++++++++------ pkg/menus/diff_menu.go | 99 +++++++++++++++++++++++++------------- pkg/menus/edit_menu.go | 63 +++++++++++++++++++----- pkg/menus/menu.go | 53 ++++++++++---------- pkg/query/filter.go | 30 ------------ pkg/settings/exe/exec.go | 4 ++ preparer.go | 87 +++++++++++++++++++++++++-------- print.go | 13 ++--- sync.go | 10 ++-- upgrade.go | 3 +- vcs.go | 6 +-- 17 files changed, 387 insertions(+), 194 deletions(-) create mode 100644 pkg/db/ialpm/high_level.go diff --git a/clean.go b/clean.go index 1a9643ae..81f4971d 100644 --- a/clean.go +++ b/clean.go @@ -99,7 +99,7 @@ func cleanAUR(ctx context.Context, keepInstalled, keepCurrent, removeAll bool, d installedBases := make(stringset.StringSet) inAURBases := make(stringset.StringSet) - remotePackages, _ := query.GetRemotePackages(dbExecutor) + remotePackages := dbExecutor.InstalledRemotePackages() files, err := os.ReadDir(config.BuildDir) if err != nil { @@ -194,10 +194,11 @@ func isGitRepository(dir string) bool { return !os.IsNotExist(err) } -func cleanAfter(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgbuildDirs []string) { +func cleanAfter(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgbuildDirs map[string]string) { fmt.Println(gotext.Get("removing untracked AUR files from cache...")) - for i, dir := range pkgbuildDirs { + i := 0 + for _, dir := range pkgbuildDirs { text.OperationInfoln(gotext.Get("Cleaning (%d/%d): %s", i+1, len(pkgbuildDirs), text.Cyan(dir))) _, stderr, err := cmdBuilder.Capture( @@ -212,5 +213,7 @@ func cleanAfter(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgbuildDirs [] ctx, dir, "clean", "-fx", "--exclude='*.pkg.*'")); err != nil { fmt.Fprintln(os.Stderr, err) } + + i++ } } diff --git a/install.go b/install.go index 5779c588..d0fb48bf 100644 --- a/install.go +++ b/install.go @@ -12,6 +12,7 @@ import ( alpm "github.com/Jguer/go-alpm/v2" gosrc "github.com/Morganamilo/go-srcinfo" + mapset "github.com/deckarep/golang-set/v2" "github.com/leonelquinteros/gotext" "github.com/Jguer/yay/v11/pkg/completion" @@ -109,12 +110,10 @@ func install(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Execu return errRefresh } - localNames, remoteNames, err := query.GetPackageNamesBySource(dbExecutor) - if err != nil { - return err - } + remoteNames := dbExecutor.InstalledRemotePackageNames() + localNames := dbExecutor.InstalledSyncPackageNames() - remoteNamesCache := stringset.FromSlice(remoteNames) + remoteNamesCache := mapset.NewThreadUnsafeSet(remoteNames...) localNamesCache := stringset.FromSlice(localNames) requestTargets := cmdArgs.Copy().Targets @@ -197,17 +196,17 @@ func install(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Execu do.Print() fmt.Println() + pkgbuildDirs := make(map[string]string, len(do.Aur)) + + for _, base := range do.Aur { + dir := filepath.Join(config.BuildDir, base.Pkgbase()) + if isGitRepository(dir) { + pkgbuildDirs[base.Pkgbase()] = dir + } + } + if config.CleanAfter { defer func() { - pkgbuildDirs := make([]string, 0, len(do.Aur)) - - for _, base := range do.Aur { - dir := filepath.Join(config.BuildDir, base.Pkgbase()) - if isGitRepository(dir) { - pkgbuildDirs = append(pkgbuildDirs, dir) - } - } - cleanAfter(ctx, config.Runtime.CmdBuilder, pkgbuildDirs) }() } @@ -230,8 +229,8 @@ func install(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Execu } } - if errCleanMenu := menus.Clean(config.CleanMenu, - config.BuildDir, do.Aur, + if errCleanMenu := menus.Clean(os.Stdout, config.CleanMenu, + pkgbuildDirs, remoteNamesCache, settings.NoConfirm, config.AnswerClean); errCleanMenu != nil { if errors.As(errCleanMenu, &settings.ErrUserAbort{}) { return errCleanMenu @@ -261,8 +260,8 @@ func install(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Execu return errA } - if errDiffMenu := menus.Diff(ctx, config.Runtime.CmdBuilder, config.BuildDir, - config.DiffMenu, do.Aur, remoteNamesCache, + if errDiffMenu := menus.Diff(ctx, config.Runtime.CmdBuilder, os.Stdout, pkgbuildDirs, + config.DiffMenu, remoteNamesCache, cloned, settings.NoConfirm, config.AnswerDiff); errDiffMenu != nil { if errors.As(errDiffMenu, &settings.ErrUserAbort{}) { return errDiffMenu @@ -280,7 +279,7 @@ func install(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Execu return err } - if errEditMenu := menus.Edit(config.EditMenu, config.BuildDir, do.Aur, + if errEditMenu := menus.Edit(os.Stdout, config.EditMenu, pkgbuildDirs, config.Editor, config.EditorFlags, remoteNamesCache, srcinfos, settings.NoConfirm, config.AnswerEdit); errEditMenu != nil { if errors.As(errEditMenu, &settings.ErrUserAbort{}) { @@ -315,7 +314,7 @@ func install(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Execu exp := make([]string, 0) for _, pkg := range do.Repo { - if !dp.Explicit.Get(pkg.Name()) && !localNamesCache.Get(pkg.Name()) && !remoteNamesCache.Get(pkg.Name()) { + if !dp.Explicit.Get(pkg.Name()) && !localNamesCache.Get(pkg.Name()) && !remoteNamesCache.Contains(pkg.Name()) { deps = append(deps, pkg.Name()) continue @@ -630,10 +629,8 @@ func buildInstallPkgbuilds( settings.NoConfirm = true // remotenames: names of all non repo packages on the system - localNames, remoteNames, err := query.GetPackageNamesBySource(dbExecutor) - if err != nil { - return err - } + remoteNames := dbExecutor.InstalledRemotePackageNames() + localNames := dbExecutor.InstalledSyncPackageNames() // cache as a stringset. maybe make it return a string set in the first // place @@ -688,7 +685,7 @@ func buildInstallPkgbuilds( } // pkgver bump - if err = config.Runtime.CmdBuilder.Show( + if err := config.Runtime.CmdBuilder.Show( config.Runtime.CmdBuilder.BuildMakepkgCmd(ctx, dir, args...)); err != nil { return errors.New(gotext.Get("error making: %s", base.String())) } @@ -727,7 +724,7 @@ func buildInstallPkgbuilds( } if installed { - err = config.Runtime.CmdBuilder.Show( + err := config.Runtime.CmdBuilder.Show( config.Runtime.CmdBuilder.BuildMakepkgCmd(ctx, dir, "-c", "--nobuild", "--noextract", "--ignorearch")) if err != nil { @@ -741,7 +738,7 @@ func buildInstallPkgbuilds( } if built { - err = config.Runtime.CmdBuilder.Show( + err := config.Runtime.CmdBuilder.Show( config.Runtime.CmdBuilder.BuildMakepkgCmd(ctx, dir, "-c", "--nobuild", "--noextract", "--ignorearch")) if err != nil { @@ -816,7 +813,7 @@ func buildInstallPkgbuilds( settings.NoConfirm = oldConfirm - return err + return nil } func installPkgArchive(ctx context.Context, cmdArgs *parser.Arguments, pkgArchives []string) error { diff --git a/pkg/db/executor.go b/pkg/db/executor.go index 63f70475..ff2403ad 100644 --- a/pkg/db/executor.go +++ b/pkg/db/executor.go @@ -30,6 +30,9 @@ type Executor interface { AlpmArchitectures() ([]string, error) BiggestPackages() []IPackage Cleanup() + InstalledRemotePackageNames() []string + InstalledRemotePackages() []IPackage + InstalledSyncPackageNames() []string IsCorrectVersionInstalled(string, string) bool LastBuildTime() time.Time LocalPackage(string) IPackage diff --git a/pkg/db/ialpm/alpm.go b/pkg/db/ialpm/alpm.go index df8b8a1a..8931045e 100644 --- a/pkg/db/ialpm/alpm.go +++ b/pkg/db/ialpm/alpm.go @@ -23,16 +23,29 @@ type AlpmExecutor struct { syncDB alpm.IDBList syncDBsCache []alpm.IDB conf *pacmanconf.Config + + installedRemotePkgs []alpm.IPackage + installedRemotePkgNames []string + installedSyncPkgNames []string } func NewExecutor(pacmanConf *pacmanconf.Config) (*AlpmExecutor, error) { - ae := &AlpmExecutor{conf: pacmanConf} + ae := &AlpmExecutor{ + handle: nil, + localDB: nil, + syncDB: nil, + syncDBsCache: []alpm.IDB{}, + conf: pacmanConf, + installedRemotePkgs: nil, + installedRemotePkgNames: nil, + installedSyncPkgNames: nil, + } - err := ae.RefreshHandle() - if err != nil { + if err := ae.RefreshHandle(); err != nil { return nil, err } + var err error ae.localDB, err = ae.handle.LocalDB() if err != nil { return nil, err diff --git a/pkg/db/ialpm/high_level.go b/pkg/db/ialpm/high_level.go new file mode 100644 index 00000000..430b696d --- /dev/null +++ b/pkg/db/ialpm/high_level.go @@ -0,0 +1,46 @@ +package ialpm + +import ( + "github.com/Jguer/yay/v11/pkg/db" + "github.com/Jguer/yay/v11/pkg/text" +) + +// GetPackageNamesBySource returns package names with and without correspondence in SyncDBS respectively. +func (ae *AlpmExecutor) getPackageNamesBySource() { + for _, localpkg := range ae.LocalPackages() { + pkgName := localpkg.Name() + if ae.SyncPackage(pkgName) != nil { + ae.installedSyncPkgNames = append(ae.installedSyncPkgNames, pkgName) + } else { + ae.installedRemotePkgs = append(ae.installedRemotePkgs, localpkg) + ae.installedRemotePkgNames = append(ae.installedRemotePkgNames, pkgName) + } + } + + text.Debugln("populating db executor package caches.", + "sync_len", len(ae.installedSyncPkgNames), "remote_len", len(ae.installedRemotePkgNames)) +} + +func (ae *AlpmExecutor) InstalledRemotePackages() []db.IPackage { + if ae.installedRemotePkgs == nil { + ae.getPackageNamesBySource() + } + + return ae.installedRemotePkgs +} + +func (ae *AlpmExecutor) InstalledRemotePackageNames() []string { + if ae.installedRemotePkgNames == nil { + ae.getPackageNamesBySource() + } + + return ae.installedRemotePkgNames +} + +func (ae *AlpmExecutor) InstalledSyncPackageNames() []string { + if ae.installedSyncPkgNames == nil { + ae.getPackageNamesBySource() + } + + return ae.installedSyncPkgNames +} diff --git a/pkg/db/mock/executor.go b/pkg/db/mock/executor.go index b2a3cf55..d9c31619 100644 --- a/pkg/db/mock/executor.go +++ b/pkg/db/mock/executor.go @@ -14,7 +14,9 @@ type ( Upgrade = db.Upgrade ) -type DBExecutor struct{} +type DBExecutor struct { + db.Executor +} func (t DBExecutor) AlpmArchitectures() ([]string, error) { panic("implement me") diff --git a/pkg/menus/clean_menu.go b/pkg/menus/clean_menu.go index 828e088f..83898e09 100644 --- a/pkg/menus/clean_menu.go +++ b/pkg/menus/clean_menu.go @@ -2,22 +2,20 @@ package menus import ( + "context" "fmt" + "io" "os" - "path/filepath" + mapset "github.com/deckarep/golang-set/v2" "github.com/leonelquinteros/gotext" - "github.com/Jguer/yay/v11/pkg/dep" - "github.com/Jguer/yay/v11/pkg/stringset" + "github.com/Jguer/yay/v11/pkg/settings" "github.com/Jguer/yay/v11/pkg/text" ) -func anyExistInCache(buildDir string, bases []dep.Base) bool { - for _, base := range bases { - pkg := base.Pkgbase() - dir := filepath.Join(buildDir, pkg) - +func anyExistInCache(pkgbuildDirs map[string]string) bool { + for _, dir := range pkgbuildDirs { if _, err := os.Stat(dir); !os.IsNotExist(err) { return true } @@ -26,14 +24,13 @@ func anyExistInCache(buildDir string, bases []dep.Base) bool { return false } -func Clean(cleanMenuOption bool, buildDir string, bases []dep.Base, - installed stringset.StringSet, noConfirm bool, answerClean string) error { - if !(cleanMenuOption && anyExistInCache(buildDir, bases)) { +func CleanFn(ctx context.Context, config *settings.Configuration, w io.Writer, pkgbuildDirsByBase map[string]string) error { + if !anyExistInCache(pkgbuildDirsByBase) { return nil } skipFunc := func(pkg string) bool { - dir := filepath.Join(buildDir, pkg) + dir := pkgbuildDirsByBase[pkg] if _, err := os.Stat(dir); os.IsNotExist(err) { return true } @@ -41,14 +38,68 @@ func Clean(cleanMenuOption bool, buildDir string, bases []dep.Base, return false } - toClean, errClean := selectionMenu(buildDir, bases, installed, gotext.Get("Packages to cleanBuild?"), + bases := make([]string, 0, len(pkgbuildDirsByBase)) + for pkg := range pkgbuildDirsByBase { + bases = append(bases, pkg) + } + + // TOFIX: empty installed slice means installed filter is disabled + toClean, errClean := selectionMenu(w, pkgbuildDirsByBase, bases, mapset.NewSet[string](), + gotext.Get("Packages to cleanBuild?"), + settings.NoConfirm, config.AnswerClean, skipFunc) + if errClean != nil { + return errClean + } + + for i, base := range toClean { + dir := pkgbuildDirsByBase[base] + text.OperationInfoln(gotext.Get("Deleting (%d/%d): %s", i+1, len(toClean), text.Cyan(dir))) + + if err := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildGitCmd(ctx, dir, "reset", "--hard")); err != nil { + text.Warnln(gotext.Get("Unable to clean:"), dir) + + return err + } + + if err := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildGitCmd(ctx, dir, "clean", "-fdx")); err != nil { + text.Warnln(gotext.Get("Unable to clean:"), dir) + + return err + } + } + + return nil +} + +func Clean(w io.Writer, cleanMenuOption bool, pkgbuildDirs map[string]string, + installed mapset.Set[string], noConfirm bool, answerClean string, +) error { + if !(cleanMenuOption && anyExistInCache(pkgbuildDirs)) { + return nil + } + + skipFunc := func(pkg string) bool { + dir := pkgbuildDirs[pkg] + if _, err := os.Stat(dir); os.IsNotExist(err) { + return true + } + + return false + } + + bases := make([]string, 0, len(pkgbuildDirs)) + for pkg := range pkgbuildDirs { + bases = append(bases, pkg) + } + + toClean, errClean := selectionMenu(w, pkgbuildDirs, bases, installed, gotext.Get("Packages to cleanBuild?"), noConfirm, answerClean, skipFunc) if errClean != nil { return errClean } for i, base := range toClean { - dir := filepath.Join(buildDir, base.Pkgbase()) + dir := pkgbuildDirs[base] text.OperationInfoln(gotext.Get("Deleting (%d/%d): %s", i+1, len(toClean), text.Cyan(dir))) if err := os.RemoveAll(dir); err != nil { diff --git a/pkg/menus/diff_menu.go b/pkg/menus/diff_menu.go index d3e499e8..60669cea 100644 --- a/pkg/menus/diff_menu.go +++ b/pkg/menus/diff_menu.go @@ -4,17 +4,16 @@ package menus import ( "context" "fmt" + "io" "os" - "path/filepath" "strings" + mapset "github.com/deckarep/golang-set/v2" "github.com/leonelquinteros/gotext" - "github.com/Jguer/yay/v11/pkg/dep" "github.com/Jguer/yay/v11/pkg/multierror" "github.com/Jguer/yay/v11/pkg/settings" "github.com/Jguer/yay/v11/pkg/settings/exe" - "github.com/Jguer/yay/v11/pkg/stringset" "github.com/Jguer/yay/v11/pkg/text" ) @@ -23,24 +22,23 @@ const ( gitDiffRefName = "AUR_SEEN" ) -func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, buildDir string, bases []dep.Base, cloned map[string]bool) error { +func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, + pkgbuildDirs map[string]string, bases []string, +) error { var errMulti multierror.MultiError - for _, base := range bases { - pkg := base.Pkgbase() - dir := filepath.Join(buildDir, pkg) + for _, pkg := range bases { + dir := pkgbuildDirs[pkg] - start, err := getLastSeenHash(ctx, cmdBuilder, buildDir, pkg) + start, err := getLastSeenHash(ctx, cmdBuilder, dir) if err != nil { errMulti.Add(err) continue } - if cloned[pkg] { - start = gitEmptyTree - } else { - hasDiff, err := gitHasDiff(ctx, cmdBuilder, buildDir, pkg) + if start != gitEmptyTree { + hasDiff, err := gitHasDiff(ctx, cmdBuilder, dir) if err != nil { errMulti.Add(err) @@ -48,7 +46,7 @@ func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, buildDir } if !hasDiff { - text.Warnln(gotext.Get("%s: No changes -- skipping", text.Cyan(base.String()))) + text.Warnln(gotext.Get("%s: No changes -- skipping", text.Cyan(pkg))) continue } @@ -73,10 +71,10 @@ func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, buildDir // Check whether or not a diff exists between the last reviewed diff and // HEAD@{upstream}. -func gitHasDiff(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, name string) (bool, error) { - if gitHasLastSeenRef(ctx, cmdBuilder, path, name) { +func gitHasDiff(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) (bool, error) { + if gitHasLastSeenRef(ctx, cmdBuilder, dir) { stdout, stderr, err := cmdBuilder.Capture( - cmdBuilder.BuildGitCmd(ctx, filepath.Join(path, name), "rev-parse", gitDiffRefName, "HEAD@{upstream}")) + cmdBuilder.BuildGitCmd(ctx, dir, "rev-parse", gitDiffRefName, "HEAD@{upstream}")) if err != nil { return false, fmt.Errorf("%s%s", stderr, err) } @@ -94,21 +92,21 @@ func gitHasDiff(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, name stri // Return wether or not we have reviewed a diff yet. It checks for the existence of // YAY_DIFF_REVIEW in the git ref-list. -func gitHasLastSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, name string) bool { +func gitHasLastSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) bool { _, _, err := cmdBuilder.Capture( cmdBuilder.BuildGitCmd(ctx, - filepath.Join(path, name), "rev-parse", "--quiet", "--verify", gitDiffRefName)) + dir, "rev-parse", "--quiet", "--verify", gitDiffRefName)) return err == nil } // Returns the last reviewed hash. If YAY_DIFF_REVIEW exists it will return this hash. // If it does not it will return empty tree as no diff have been reviewed yet. -func getLastSeenHash(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, name string) (string, error) { - if gitHasLastSeenRef(ctx, cmdBuilder, path, name) { +func getLastSeenHash(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) (string, error) { + if gitHasLastSeenRef(ctx, cmdBuilder, dir) { stdout, stderr, err := cmdBuilder.Capture( cmdBuilder.BuildGitCmd(ctx, - filepath.Join(path, name), "rev-parse", gitDiffRefName)) + dir, "rev-parse", gitDiffRefName)) if err != nil { return "", fmt.Errorf("%s %s", stderr, err) } @@ -123,10 +121,10 @@ func getLastSeenHash(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, name // Update the YAY_DIFF_REVIEW ref to HEAD. We use this ref to determine which diff were // reviewed by the user. -func gitUpdateSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, name string) error { +func gitUpdateSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) error { _, stderr, err := cmdBuilder.Capture( cmdBuilder.BuildGitCmd(ctx, - filepath.Join(path, name), "update-ref", gitDiffRefName, "HEAD")) + dir, "update-ref", gitDiffRefName, "HEAD")) if err != nil { return fmt.Errorf("%s %s", stderr, err) } @@ -134,13 +132,12 @@ func gitUpdateSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, nam return nil } -func updatePkgbuildSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, buildDir string, bases []dep.Base) error { +func updatePkgbuildSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgbuildDirs map[string]string, bases []string) error { var errMulti multierror.MultiError - for _, base := range bases { - pkg := base.Pkgbase() - - if err := gitUpdateSeenRef(ctx, cmdBuilder, buildDir, pkg); err != nil { + for _, pkg := range bases { + dir := pkgbuildDirs[pkg] + if err := gitUpdateSeenRef(ctx, cmdBuilder, dir); err != nil { errMulti.Add(err) } } @@ -148,21 +145,26 @@ func updatePkgbuildSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, buil return errMulti.Return() } -func Diff(ctx context.Context, cmdBuilder exe.ICmdBuilder, - buildDir string, diffMenuOption bool, bases []dep.Base, - installed stringset.StringSet, cloned map[string]bool, noConfirm bool, diffDefaultAnswer string, +func Diff(ctx context.Context, cmdBuilder exe.ICmdBuilder, w io.Writer, + pkgbuildDirs map[string]string, diffMenuOption bool, + installed mapset.Set[string], cloned map[string]bool, noConfirm bool, diffDefaultAnswer string, ) error { if !diffMenuOption { return nil } - toDiff, errMenu := selectionMenu(buildDir, bases, installed, gotext.Get("Diffs to show?"), + bases := make([]string, 0, len(pkgbuildDirs)) + for base := range pkgbuildDirs { + bases = append(bases, base) + } + + toDiff, errMenu := selectionMenu(w, pkgbuildDirs, bases, installed, gotext.Get("Diffs to show?"), noConfirm, diffDefaultAnswer, nil) if errMenu != nil || len(toDiff) == 0 { return errMenu } - if errD := showPkgbuildDiffs(ctx, cmdBuilder, buildDir, toDiff, cloned); errD != nil { + if errD := showPkgbuildDiffs(ctx, cmdBuilder, pkgbuildDirs, toDiff); errD != nil { return errD } @@ -172,7 +174,36 @@ func Diff(ctx context.Context, cmdBuilder exe.ICmdBuilder, return settings.ErrUserAbort{} } - if errUpd := updatePkgbuildSeenRef(ctx, cmdBuilder, buildDir, toDiff); errUpd != nil { + if errUpd := updatePkgbuildSeenRef(ctx, cmdBuilder, pkgbuildDirs, toDiff); errUpd != nil { + return errUpd + } + + return nil +} + +func DiffFn(ctx context.Context, config *settings.Configuration, w io.Writer, pkgbuildDirsByBase map[string]string) error { + bases := make([]string, 0, len(pkgbuildDirsByBase)) + for base := range pkgbuildDirsByBase { + bases = append(bases, base) + } + + toDiff, errMenu := selectionMenu(w, pkgbuildDirsByBase, bases, mapset.NewThreadUnsafeSet[string](), gotext.Get("Diffs to show?"), + settings.NoConfirm, config.AnswerDiff, nil) + if errMenu != nil || len(toDiff) == 0 { + return errMenu + } + + if errD := showPkgbuildDiffs(ctx, config.Runtime.CmdBuilder, pkgbuildDirsByBase, toDiff); errD != nil { + return errD + } + + fmt.Println() + + if !text.ContinueTask(os.Stdin, gotext.Get("Proceed with install?"), true, false) { + return settings.ErrUserAbort{} + } + + if errUpd := updatePkgbuildSeenRef(ctx, config.Runtime.CmdBuilder, pkgbuildDirsByBase, toDiff); errUpd != nil { return errUpd } diff --git a/pkg/menus/edit_menu.go b/pkg/menus/edit_menu.go index 1614059c..097d0fa1 100644 --- a/pkg/menus/edit_menu.go +++ b/pkg/menus/edit_menu.go @@ -2,19 +2,20 @@ package menus import ( + "context" "errors" "fmt" + "io" "os" "os/exec" "path/filepath" "strings" gosrc "github.com/Morganamilo/go-srcinfo" + mapset "github.com/deckarep/golang-set/v2" "github.com/leonelquinteros/gotext" - "github.com/Jguer/yay/v11/pkg/dep" "github.com/Jguer/yay/v11/pkg/settings" - "github.com/Jguer/yay/v11/pkg/stringset" "github.com/Jguer/yay/v11/pkg/text" ) @@ -82,19 +83,20 @@ func editor(editorConfig, editorFlags string, noConfirm bool) (editor string, ar } } -func editPkgbuilds(buildDir string, bases []dep.Base, editorConfig, +func editPkgbuilds(pkgbuildDirs map[string]string, bases []string, editorConfig, editorFlags string, srcinfos map[string]*gosrc.Srcinfo, noConfirm bool, ) error { pkgbuilds := make([]string, 0, len(bases)) - for _, base := range bases { - pkg := base.Pkgbase() - dir := filepath.Join(buildDir, pkg) + for _, pkg := range bases { + dir := pkgbuildDirs[pkg] pkgbuilds = append(pkgbuilds, filepath.Join(dir, "PKGBUILD")) - for _, splitPkg := range srcinfos[pkg].SplitPackages() { - if splitPkg.Install != "" { - pkgbuilds = append(pkgbuilds, filepath.Join(dir, splitPkg.Install)) + if srcinfos != nil { + for _, splitPkg := range srcinfos[pkg].SplitPackages() { + if splitPkg.Install != "" { + pkgbuilds = append(pkgbuilds, filepath.Join(dir, splitPkg.Install)) + } } } } @@ -113,21 +115,56 @@ func editPkgbuilds(buildDir string, bases []dep.Base, editorConfig, return nil } -func Edit(editMenuOption bool, buildDir string, bases []dep.Base, editorConfig, - editorFlags string, installed stringset.StringSet, srcinfos map[string]*gosrc.Srcinfo, +func Edit(w io.Writer, editMenuOption bool, pkgbuildDirs map[string]string, editorConfig, + editorFlags string, installed mapset.Set[string], srcinfos map[string]*gosrc.Srcinfo, noConfirm bool, editDefaultAnswer string, ) error { if !editMenuOption { return nil } - toEdit, errMenu := selectionMenu(buildDir, bases, + bases := make([]string, 0, len(pkgbuildDirs)) + for pkg := range pkgbuildDirs { + bases = append(bases, pkg) + } + + toEdit, errMenu := selectionMenu(w, pkgbuildDirs, bases, installed, gotext.Get("PKGBUILDs to edit?"), noConfirm, editDefaultAnswer, nil) if errMenu != nil || len(toEdit) == 0 { return errMenu } - if errEdit := editPkgbuilds(buildDir, toEdit, editorConfig, editorFlags, srcinfos, noConfirm); errEdit != nil { + if errEdit := editPkgbuilds(pkgbuildDirs, toEdit, editorConfig, editorFlags, srcinfos, noConfirm); errEdit != nil { + return errEdit + } + + fmt.Println() + + if !text.ContinueTask(os.Stdin, gotext.Get("Proceed with install?"), true, false) { + return settings.ErrUserAbort{} + } + + return nil +} + +func EditFn(ctx context.Context, config *settings.Configuration, w io.Writer, + pkgbuildDirsByBase map[string]string, +) error { + bases := make([]string, 0, len(pkgbuildDirsByBase)) + for pkg := range pkgbuildDirsByBase { + bases = append(bases, pkg) + } + + toEdit, errMenu := selectionMenu(w, pkgbuildDirsByBase, bases, + mapset.NewThreadUnsafeSet[string](), + gotext.Get("PKGBUILDs to edit?"), settings.NoConfirm, config.AnswerEdit, nil) + if errMenu != nil || len(toEdit) == 0 { + return errMenu + } + + // TOFIX: remove or use srcinfo data + if errEdit := editPkgbuilds(pkgbuildDirsByBase, + toEdit, config.Editor, config.EditorFlags, nil, settings.NoConfirm); errEdit != nil { return errEdit } diff --git a/pkg/menus/menu.go b/pkg/menus/menu.go index ed056e60..5b6a513b 100644 --- a/pkg/menus/menu.go +++ b/pkg/menus/menu.go @@ -2,29 +2,27 @@ package menus import ( "fmt" + "io" "os" - "path/filepath" "github.com/leonelquinteros/gotext" - "github.com/Jguer/yay/v11/pkg/dep" "github.com/Jguer/yay/v11/pkg/intrange" "github.com/Jguer/yay/v11/pkg/settings" - "github.com/Jguer/yay/v11/pkg/stringset" "github.com/Jguer/yay/v11/pkg/text" + + mapset "github.com/deckarep/golang-set/v2" ) -func pkgbuildNumberMenu(buildDir string, bases []dep.Base, installed stringset.StringSet) { +func pkgbuildNumberMenu(w io.Writer, pkgbuildDirs map[string]string, bases []string, installed mapset.Set[string]) { toPrint := "" - for n, base := range bases { - pkg := base.Pkgbase() - dir := filepath.Join(buildDir, pkg) + for n, pkgBase := range bases { + dir := pkgbuildDirs[pkgBase] + toPrint += fmt.Sprintf(text.Magenta("%3d")+" %-40s", len(pkgbuildDirs)-n, + text.Bold(pkgBase)) - toPrint += fmt.Sprintf(text.Magenta("%3d")+" %-40s", len(bases)-n, - text.Bold(base.String())) - - if base.AnyIsInSet(installed) { + if installed.Contains(pkgBase) { toPrint += text.Bold(text.Green(gotext.Get(" (Installed)"))) } @@ -35,14 +33,15 @@ func pkgbuildNumberMenu(buildDir string, bases []dep.Base, installed stringset.S toPrint += "\n" } - fmt.Print(toPrint) + fmt.Fprint(w, toPrint) } -func selectionMenu(buildDir string, bases []dep.Base, installed stringset.StringSet, - message string, noConfirm bool, defaultAnswer string, skipFunc func(string) bool) ([]dep.Base, error) { - selected := make([]dep.Base, 0) +func selectionMenu(w io.Writer, pkgbuildDirs map[string]string, bases []string, installed mapset.Set[string], + message string, noConfirm bool, defaultAnswer string, skipFunc func(string) bool, +) ([]string, error) { + selected := make([]string, 0) - pkgbuildNumberMenu(buildDir, bases, installed) + pkgbuildNumberMenu(w, pkgbuildDirs, bases, installed) text.Infoln(message) text.Infoln(gotext.Get("%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)", text.Cyan(gotext.Get("[N]one")))) @@ -63,40 +62,38 @@ func selectionMenu(buildDir string, bases []dep.Base, installed stringset.String return selected, nil } - for i, base := range bases { - pkg := base.Pkgbase() - - if skipFunc != nil && skipFunc(pkg) { + for i, pkgBase := range bases { + if skipFunc != nil && skipFunc(pkgBase) { continue } - anyInstalled := base.AnyIsInSet(installed) + anyInstalled := installed.Contains(pkgBase) if !eIsInclude && eExclude.Get(len(bases)-i) { continue } if anyInstalled && (eOtherInclude.Get("i") || eOtherInclude.Get("installed")) { - selected = append(selected, base) + selected = append(selected, pkgBase) continue } if !anyInstalled && (eOtherInclude.Get("no") || eOtherInclude.Get("notinstalled")) { - selected = append(selected, base) + selected = append(selected, pkgBase) continue } if eOtherInclude.Get("a") || eOtherInclude.Get("all") { - selected = append(selected, base) + selected = append(selected, pkgBase) continue } - if eIsInclude && (eInclude.Get(len(bases)-i) || eOtherInclude.Get(pkg)) { - selected = append(selected, base) + if eIsInclude && (eInclude.Get(len(bases)-i) || eOtherInclude.Get(pkgBase)) { + selected = append(selected, pkgBase) } - if !eIsInclude && (!eExclude.Get(len(bases)-i) && !eOtherExclude.Get(pkg)) { - selected = append(selected, base) + if !eIsInclude && (!eExclude.Get(len(bases)-i) && !eOtherExclude.Get(pkgBase)) { + selected = append(selected, pkgBase) } } diff --git a/pkg/query/filter.go b/pkg/query/filter.go index e03ba79c..518192b3 100644 --- a/pkg/query/filter.go +++ b/pkg/query/filter.go @@ -3,40 +3,10 @@ package query import ( "github.com/leonelquinteros/gotext" - "github.com/Jguer/yay/v11/pkg/db" "github.com/Jguer/yay/v11/pkg/settings/parser" "github.com/Jguer/yay/v11/pkg/text" ) -// GetPackageNamesBySource returns package names with and without correspondence in SyncDBS respectively. -func GetPackageNamesBySource(dbExecutor db.Executor) (local, remote []string, err error) { - for _, localpkg := range dbExecutor.LocalPackages() { - pkgName := localpkg.Name() - if dbExecutor.SyncPackage(pkgName) != nil { - local = append(local, pkgName) - } else { - remote = append(remote, pkgName) - } - } - - return local, remote, err -} - -// GetRemotePackages returns packages with no correspondence in SyncDBS. -func GetRemotePackages(dbExecutor db.Executor) ( - remote []db.IPackage, - remoteNames []string) { - for _, localpkg := range dbExecutor.LocalPackages() { - pkgName := localpkg.Name() - if dbExecutor.SyncPackage(pkgName) == nil { - remote = append(remote, localpkg) - remoteNames = append(remoteNames, pkgName) - } - } - - return remote, remoteNames -} - func RemoveInvalidTargets(targets []string, mode parser.TargetMode) []string { filteredTargets := make([]string, 0) diff --git a/pkg/settings/exe/exec.go b/pkg/settings/exe/exec.go index ad43d40b..f85c67f0 100644 --- a/pkg/settings/exe/exec.go +++ b/pkg/settings/exe/exec.go @@ -4,6 +4,8 @@ import ( "os" "os/exec" "strings" + + "github.com/Jguer/yay/v11/pkg/text" ) type Runner interface { @@ -15,10 +17,12 @@ type OSRunner struct{} func (r *OSRunner) Show(cmd *exec.Cmd) error { cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr + text.Debugln("running", cmd.String()) return cmd.Run() } func (r *OSRunner) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) { + text.Debugln("capturing", cmd.String()) outbuf, err := cmd.Output() stdout = strings.TrimSpace(string(outbuf)) diff --git a/preparer.go b/preparer.go index de1170aa..8d5bf2d7 100644 --- a/preparer.go +++ b/preparer.go @@ -11,36 +11,59 @@ import ( "github.com/Jguer/yay/v11/pkg/db" "github.com/Jguer/yay/v11/pkg/dep" "github.com/Jguer/yay/v11/pkg/download" + "github.com/Jguer/yay/v11/pkg/menus" "github.com/Jguer/yay/v11/pkg/settings" "github.com/Jguer/yay/v11/pkg/settings/exe" "github.com/Jguer/yay/v11/pkg/text" + gosrc "github.com/Morganamilo/go-srcinfo" mapset "github.com/deckarep/golang-set/v2" "github.com/leonelquinteros/gotext" ) +type PostDownloadHookFunc func(ctx context.Context, config *settings.Configuration, w io.Writer, pkgbuildDirsByBase map[string]string) error + type Preparer struct { - dbExecutor db.Executor - cmdBuilder exe.ICmdBuilder - config *settings.Configuration + dbExecutor db.Executor + cmdBuilder exe.ICmdBuilder + config *settings.Configuration + postDownloadHooks []PostDownloadHookFunc makeDeps []string } +func NewPreparer(dbExecutor db.Executor, cmdBuilder exe.ICmdBuilder, config *settings.Configuration) *Preparer { + preper := &Preparer{ + dbExecutor: dbExecutor, + cmdBuilder: cmdBuilder, + config: config, + postDownloadHooks: []PostDownloadHookFunc{}, + } + + if config.CleanMenu { + preper.postDownloadHooks = append(preper.postDownloadHooks, menus.CleanFn) + } + + if config.DiffMenu { + preper.postDownloadHooks = append(preper.postDownloadHooks, menus.DiffFn) + } + + if config.EditMenu { + preper.postDownloadHooks = append(preper.postDownloadHooks, menus.EditFn) + } + + return preper +} + func (preper *Preparer) ShouldCleanAURDirs(pkgBuildDirs map[string]string) PostInstallHookFunc { if !preper.config.CleanAfter { return nil } - dirs := make([]string, 0, len(pkgBuildDirs)) - for _, dir := range pkgBuildDirs { - dirs = append(dirs, dir) - } - - text.Debugln("added post install hook to clean up AUR dirs", dirs) + text.Debugln("added post install hook to clean up AUR dirs", pkgBuildDirs) return func(ctx context.Context) error { - cleanAfter(ctx, preper.config.Runtime.CmdBuilder, dirs) + cleanAfter(ctx, preper.config.Runtime.CmdBuilder, pkgBuildDirs) return nil } } @@ -109,30 +132,56 @@ func (preper *Preparer) Present(w io.Writer, targets []map[string]*dep.InstallIn } func (preper *Preparer) PrepareWorkspace(ctx context.Context, targets []map[string]*dep.InstallInfo) (map[string]string, error) { - aurBases := mapset.NewThreadUnsafeSet[string]() - pkgBuildDirs := make(map[string]string, 0) + aurBasesToClone := mapset.NewThreadUnsafeSet[string]() + pkgBuildDirsByBase := make(map[string]string, len(targets)) for _, layer := range targets { - for pkgName, info := range layer { + for _, info := range layer { if info.Source == dep.AUR { pkgBase := *info.AURBase - aurBases.Add(pkgBase) - pkgBuildDirs[pkgName] = filepath.Join(config.BuildDir, pkgBase) + pkgBuildDir := filepath.Join(preper.config.BuildDir, pkgBase) + if preper.needToCloneAURBase(info, pkgBuildDir) { + aurBasesToClone.Add(pkgBase) + } + pkgBuildDirsByBase[pkgBase] = pkgBuildDir } else if info.Source == dep.SrcInfo { - pkgBuildDirs[pkgName] = *info.SrcinfoPath + pkgBase := *info.AURBase + pkgBuildDirsByBase[pkgBase] = *info.SrcinfoPath } } } if _, errA := download.AURPKGBUILDRepos(ctx, - preper.cmdBuilder, aurBases.ToSlice(), config.AURURL, config.BuildDir, false); errA != nil { + preper.cmdBuilder, aurBasesToClone.ToSlice(), + config.AURURL, config.BuildDir, false); errA != nil { return nil, errA } if errP := downloadPKGBUILDSourceFanout(ctx, config.Runtime.CmdBuilder, - pkgBuildDirs, false, config.MaxConcurrentDownloads); errP != nil { + pkgBuildDirsByBase, false, config.MaxConcurrentDownloads); errP != nil { text.Errorln(errP) } - return pkgBuildDirs, nil + for _, hookFn := range preper.postDownloadHooks { + if err := hookFn(ctx, preper.config, os.Stdout, pkgBuildDirsByBase); err != nil { + return nil, err + } + } + + return pkgBuildDirsByBase, nil +} + +func (preper *Preparer) needToCloneAURBase(installInfo *dep.InstallInfo, pkgbuildDir string) bool { + if preper.config.ReDownload == "all" { + return true + } + + srcinfoFile := filepath.Join(pkgbuildDir, ".SRCINFO") + if pkgbuild, err := gosrc.ParseFile(srcinfoFile); err == nil { + if db.VerCmp(pkgbuild.Version(), installInfo.Version) >= 0 { + return false + } + } + + return true } diff --git a/print.go b/print.go index 52b5782d..e1b88e7e 100644 --- a/print.go +++ b/print.go @@ -73,11 +73,7 @@ func biggestPackages(dbExecutor db.Executor) { func localStatistics(ctx context.Context, dbExecutor db.Executor) error { info := statistics(dbExecutor) - _, remoteNames, err := query.GetPackageNamesBySource(dbExecutor) - if err != nil { - return err - } - + remoteNames := dbExecutor.InstalledRemotePackageNames() text.Infoln(gotext.Get("Yay version v%s", yayVersion)) fmt.Println(text.Bold(text.Cyan("==========================================="))) text.Infoln(gotext.Get("Total installed packages: %s", text.Cyan(strconv.Itoa(info.Totaln)))) @@ -124,11 +120,8 @@ func printUpdateList(ctx context.Context, cmdArgs *parser.Arguments, old := os.Stdout // keep backup of the real stdout os.Stdout = nil - localNames, remoteNames, err := query.GetPackageNamesBySource(dbExecutor) - if err != nil { - os.Stdout = old - return err - } + remoteNames := dbExecutor.InstalledRemotePackageNames() + localNames := dbExecutor.InstalledSyncPackageNames() aurUp, repoUp, err := upList(ctx, nil, warnings, dbExecutor, enableDowngrade, filter) os.Stdout = old // restoring the real stdout diff --git a/sync.go b/sync.go index 465e4881..af9b6236 100644 --- a/sync.go +++ b/sync.go @@ -52,11 +52,7 @@ func syncInstall(ctx context.Context, topoSorted := graph.TopoSortedLayerMap() - preparer := &Preparer{ - dbExecutor: dbExecutor, - cmdBuilder: config.Runtime.CmdBuilder, - config: config, - } + preparer := NewPreparer(dbExecutor, config.Runtime.CmdBuilder, config) installer := &Installer{dbExecutor: dbExecutor} if errP := preparer.Present(os.Stdout, topoSorted); errP != nil { @@ -73,6 +69,10 @@ func syncInstall(ctx context.Context, return err } + if cleanAURDirsFunc := preparer.ShouldCleanAURDirs(pkgBuildDirs); cleanAURDirsFunc != nil { + installer.AddPostInstallHook(cleanAURDirsFunc) + } + err = installer.Install(ctx, cmdArgs, topoSorted, pkgBuildDirs) if err != nil { if errHook := installer.RunPostInstallHooks(ctx); errHook != nil { diff --git a/upgrade.go b/upgrade.go index a93cf7a3..cf7c7b11 100644 --- a/upgrade.go +++ b/upgrade.go @@ -41,7 +41,8 @@ func upList(ctx context.Context, aurCache *metadata.Client, warnings *query.AURWarnings, dbExecutor db.Executor, enableDowngrade bool, filter upgrade.Filter, ) (aurUp, repoUp upgrade.UpSlice, err error) { - remote, remoteNames := query.GetRemotePackages(dbExecutor) + remote := dbExecutor.InstalledRemotePackages() + remoteNames := dbExecutor.InstalledRemotePackageNames() var ( wg sync.WaitGroup diff --git a/vcs.go b/vcs.go index 2a97fdaf..609ea0af 100644 --- a/vcs.go +++ b/vcs.go @@ -23,11 +23,7 @@ func createDevelDB(ctx context.Context, config *settings.Configuration, dbExecut wg sync.WaitGroup ) - _, remoteNames, err := query.GetPackageNamesBySource(dbExecutor) - if err != nil { - return err - } - + remoteNames := dbExecutor.InstalledRemotePackageNames() info, err := query.AURInfoPrint(ctx, config.Runtime.AURClient, remoteNames, config.RequestSplitN) if err != nil { return err