diff --git a/.golangci.yml b/.golangci.yml index 3bdce91..d19fa9b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -47,6 +47,7 @@ linters: # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint disable-all: true enable: + - forbidigo - bodyclose - dogsled - dupl @@ -80,8 +81,12 @@ linters: - whitespace run: - go: "1.18" + go: "1.20" timeout: "10m" + forbidigo: + forbid: + - p: ^fmt\.Print.*$ + msg: Do not commit print statements. issues: exclude-rules: diff --git a/README.md b/README.md index 08123d4..0262d54 100644 --- a/README.md +++ b/README.md @@ -111,17 +111,6 @@ pacman -S --needed git base-devel yay Make sure you have the `Color` option in your `/etc/pacman.conf` (see issue [#123](https://github.com/Jguer/yay/issues/123)). -- **Yay is not prompting to skip packages during system upgrade.** - - The default behavior was changed after - [v8.918](https://github.com/Jguer/yay/releases/tag/v8.918) - (see [3bdb534](https://github.com/Jguer/yay/commit/3bdb5343218d99d40f8a449b887348611f6bdbfc) - and issue [#554](https://github.com/Jguer/yay/issues/554)). - To restore the package-skip behavior use `--combinedupgrade` (make - it permanent by appending `--save`). Note: skipping packages will leave your - system in a - [partially-upgraded state](https://wiki.archlinux.org/index.php/System_maintenance#Partial_upgrades_are_unsupported). - - **Sometimes diffs are printed to the terminal, and other times they are paged via less. How do I fix this?** Yay uses `git diff` to display diffs, which by default tells less not to @@ -137,7 +126,7 @@ pacman -S --needed git base-devel yay `yay -{OPERATION} --aur` `yay -{OPERATION} --repo` -- **An `Out Of Date AUR Packages` message is displayed. Why doesn't Yay update them?** +- **A `Flagged Out Of Date AUR Packages` message is displayed. Why doesn't Yay update them?** This message does not mean that updated AUR packages are available. It means the packages have been flagged out of date on the AUR, but @@ -159,21 +148,6 @@ pacman -S --needed git base-devel yay Check [CONTRIBUTING.md](./CONTRIBUTING.md) for more information. -- **What settings do you use?** - - ```sh - yay -Y --devel --combinedupgrade --batchinstall --save - ``` - - Pacman conf options: - - ```conf - UseSyslog - Color - CheckSpace - VerbosePkgLists - ``` - ## Support All support related to Yay should be requested via GitHub issues. Since Yay is not diff --git a/clean.go b/clean.go index aed65d0..4a15782 100644 --- a/clean.go +++ b/clean.go @@ -2,7 +2,6 @@ package main import ( "context" - "fmt" "os" "path/filepath" @@ -11,10 +10,10 @@ import ( "github.com/leonelquinteros/gotext" "github.com/Jguer/yay/v12/pkg/db" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/parser" - "github.com/Jguer/yay/v12/pkg/text" ) // CleanDependencies removes all dangling dependencies in system. @@ -49,13 +48,13 @@ func cleanRemove(ctx context.Context, cfg *settings.Configuration, arguments, cfg.Mode, settings.NoConfirm)) } -func syncClean(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments, dbExecutor db.Executor) error { +func syncClean(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error { keepInstalled := false keepCurrent := false _, removeAll, _ := cmdArgs.GetArg("c", "clean") - for _, v := range cfg.Runtime.PacmanConf.CleanMethod { + for _, v := range run.PacmanConf.CleanMethod { if v == "KeepInstalled" { keepInstalled = true } else if v == "KeepCurrent" { @@ -63,14 +62,14 @@ func syncClean(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser } } - if cfg.Mode.AtLeastRepo() { - if err := cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx, - cmdArgs, cfg.Mode, settings.NoConfirm)); err != nil { + if run.Cfg.Mode.AtLeastRepo() { + if err := run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, + cmdArgs, run.Cfg.Mode, settings.NoConfirm)); err != nil { return err } } - if !cfg.Mode.AtLeastAUR() { + if !run.Cfg.Mode.AtLeastAUR() { return nil } @@ -81,10 +80,10 @@ func syncClean(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser question = gotext.Get("Do you want to remove all other AUR packages from cache?") } - fmt.Println(gotext.Get("\nBuild directory:"), cfg.BuildDir) + run.Logger.Println(gotext.Get("\nBuild directory:"), run.Cfg.BuildDir) - if text.ContinueTask(os.Stdin, question, true, settings.NoConfirm) { - if err := cleanAUR(ctx, cfg, keepInstalled, keepCurrent, removeAll, dbExecutor); err != nil { + if run.Logger.ContinueTask(question, true, settings.NoConfirm) { + if err := cleanAUR(ctx, run, keepInstalled, keepCurrent, removeAll, dbExecutor); err != nil { return err } } @@ -93,24 +92,24 @@ func syncClean(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser return nil } - if text.ContinueTask(os.Stdin, gotext.Get("Do you want to remove ALL untracked AUR files?"), true, settings.NoConfirm) { - return cleanUntracked(ctx, cfg) + if run.Logger.ContinueTask(gotext.Get("Do you want to remove ALL untracked AUR files?"), true, settings.NoConfirm) { + return cleanUntracked(ctx, run) } return nil } -func cleanAUR(ctx context.Context, cfg *settings.Configuration, +func cleanAUR(ctx context.Context, run *runtime.Runtime, keepInstalled, keepCurrent, removeAll bool, dbExecutor db.Executor, ) error { - cfg.Runtime.Logger.Println(gotext.Get("removing AUR packages from cache...")) + run.Logger.Println(gotext.Get("removing AUR packages from cache...")) installedBases := mapset.NewThreadUnsafeSet[string]() inAURBases := mapset.NewThreadUnsafeSet[string]() remotePackages := dbExecutor.InstalledRemotePackages() - files, err := os.ReadDir(cfg.BuildDir) + files, err := os.ReadDir(run.Cfg.BuildDir) if err != nil { return err } @@ -130,7 +129,7 @@ func cleanAUR(ctx context.Context, cfg *settings.Configuration, // Querying the AUR is slow and needs internet so don't do it if we // don't need to. if keepCurrent { - info, errInfo := cfg.Runtime.AURClient.Get(ctx, &aur.Query{ + info, errInfo := run.AURClient.Get(ctx, &aur.Query{ Needles: cachedPackages, }) if errInfo != nil { @@ -165,20 +164,20 @@ func cleanAUR(ctx context.Context, cfg *settings.Configuration, } } - dir := filepath.Join(cfg.BuildDir, file.Name()) - cfg.Runtime.Logger.Debugln("removing", dir) + dir := filepath.Join(run.Cfg.BuildDir, file.Name()) + run.Logger.Debugln("removing", dir) if err = os.RemoveAll(dir); err != nil { - cfg.Runtime.Logger.Warnln(gotext.Get("Unable to remove %s: %s", dir, err)) + run.Logger.Warnln(gotext.Get("Unable to remove %s: %s", dir, err)) } } return nil } -func cleanUntracked(ctx context.Context, cfg *settings.Configuration) error { - cfg.Runtime.Logger.Println(gotext.Get("removing untracked AUR files from cache...")) +func cleanUntracked(ctx context.Context, run *runtime.Runtime) error { + run.Logger.Println(gotext.Get("removing untracked AUR files from cache...")) - files, err := os.ReadDir(cfg.BuildDir) + files, err := os.ReadDir(run.Cfg.BuildDir) if err != nil { return err } @@ -188,12 +187,11 @@ func cleanUntracked(ctx context.Context, cfg *settings.Configuration) error { continue } - dir := filepath.Join(cfg.BuildDir, file.Name()) - cfg.Runtime.Logger.Debugln("cleaning", dir) + dir := filepath.Join(run.Cfg.BuildDir, file.Name()) + run.Logger.Debugln("cleaning", dir) if isGitRepository(dir) { - if err := cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildGitCmd(ctx, dir, "clean", "-fx")); err != nil { - cfg.Runtime.Logger.Warnln(gotext.Get("Unable to clean:"), dir) - + if err := run.CmdBuilder.Show(run.CmdBuilder.BuildGitCmd(ctx, dir, "clean", "-fx")); err != nil { + run.Logger.Warnln(gotext.Get("Unable to clean:"), dir) return err } } @@ -206,29 +204,3 @@ func isGitRepository(dir string) bool { _, err := os.Stat(filepath.Join(dir, ".git")) return !os.IsNotExist(err) } - -func cleanAfter(ctx context.Context, config *settings.Configuration, - cmdBuilder exe.ICmdBuilder, pkgbuildDirs map[string]string, -) { - fmt.Println(gotext.Get("removing untracked AUR files from cache...")) - - 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( - cmdBuilder.BuildGitCmd( - ctx, dir, "reset", "--hard", "HEAD")) - if err != nil { - text.Errorln(gotext.Get("error resetting %s: %s", dir, stderr)) - } - - if err := config.Runtime.CmdBuilder.Show( - config.Runtime.CmdBuilder.BuildGitCmd( - ctx, dir, "clean", "-fx", "--exclude", "*.pkg.*")); err != nil { - fmt.Fprintln(os.Stderr, err) - } - - i++ - } -} diff --git a/clean_test.go b/clean_test.go index 1d5d356..bf106c3 100644 --- a/clean_test.go +++ b/clean_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/require" "github.com/Jguer/yay/v12/pkg/db/mock" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/parser" @@ -90,15 +91,13 @@ func TestCleanHanging(t *testing.T) { Runner: mockRunner, SudoLoopEnabled: false, } - cfg := &settings.Configuration{ - Runtime: &settings.Runtime{CmdBuilder: cmdBuilder}, - } + run := &runtime.Runtime{CmdBuilder: cmdBuilder, Cfg: &settings.Configuration{}} cmdArgs := parser.MakeArguments() cmdArgs.AddArg(tc.args...) err := handleCmd(context.Background(), - cfg, cmdArgs, dbExc, + run, cmdArgs, dbExc, ) require.NoError(t, err) diff --git a/cmd.go b/cmd.go index df1a927..304bb69 100644 --- a/cmd.go +++ b/cmd.go @@ -17,6 +17,7 @@ import ( "github.com/Jguer/yay/v12/pkg/intrange" "github.com/Jguer/yay/v12/pkg/news" "github.com/Jguer/yay/v12/pkg/query" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/parser" @@ -25,8 +26,8 @@ import ( "github.com/Jguer/yay/v12/pkg/vcs" ) -func usage() { - fmt.Println(`Usage: +func usage(logger *text.Logger) { + logger.Println(`Usage: yay yay [...] yay @@ -146,50 +147,49 @@ getpkgbuild specific options: -p --print Print pkgbuild of packages`) } -func handleCmd(ctx context.Context, cfg *settings.Configuration, +func handleCmd(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor, ) error { if cmdArgs.ExistsArg("h", "help") { - return handleHelp(ctx, cfg, cmdArgs) + return handleHelp(ctx, run, cmdArgs) } - if cfg.SudoLoop && cmdArgs.NeedRoot(cfg.Mode) { - cfg.Runtime.CmdBuilder.SudoLoop() + if run.Cfg.SudoLoop && cmdArgs.NeedRoot(run.Cfg.Mode) { + run.CmdBuilder.SudoLoop() } switch cmdArgs.Op { case "V", "version": - handleVersion() - + handleVersion(run.Logger) return nil case "D", "database": - return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx, - cmdArgs, cfg.Mode, settings.NoConfirm)) + return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, + cmdArgs, run.Cfg.Mode, settings.NoConfirm)) case "F", "files": - return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx, - cmdArgs, cfg.Mode, settings.NoConfirm)) + return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, + cmdArgs, run.Cfg.Mode, settings.NoConfirm)) case "Q", "query": - return handleQuery(ctx, cfg, cmdArgs, dbExecutor) + return handleQuery(ctx, run, cmdArgs, dbExecutor) case "R", "remove": - return handleRemove(ctx, cfg, cmdArgs, cfg.Runtime.VCSStore) + return handleRemove(ctx, run, cmdArgs, run.VCSStore) case "S", "sync": - return handleSync(ctx, cfg, cmdArgs, dbExecutor) + return handleSync(ctx, run, cmdArgs, dbExecutor) case "T", "deptest": - return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx, - cmdArgs, cfg.Mode, settings.NoConfirm)) + return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, + cmdArgs, run.Cfg.Mode, settings.NoConfirm)) case "U", "upgrade": - return handleUpgrade(ctx, cfg, cmdArgs) + return handleUpgrade(ctx, run, cmdArgs) case "B", "build": - return handleBuild(ctx, cfg, dbExecutor, cmdArgs) + return handleBuild(ctx, run, dbExecutor, cmdArgs) case "G", "getpkgbuild": - return handleGetpkgbuild(ctx, cfg, cmdArgs, dbExecutor) + return handleGetpkgbuild(ctx, run, cmdArgs, dbExecutor) case "P", "show": - return handlePrint(ctx, cfg, cmdArgs, dbExecutor) + return handlePrint(ctx, run, cmdArgs, dbExecutor) case "Y", "yay": - return handleYay(ctx, cfg, cmdArgs, cfg.Runtime.CmdBuilder, - dbExecutor, cfg.Runtime.QueryBuilder) + return handleYay(ctx, run, cmdArgs, run.CmdBuilder, + dbExecutor, run.QueryBuilder) case "W", "web": - return handleWeb(ctx, cfg, cmdArgs) + return handleWeb(ctx, run, cmdArgs) } return errors.New(gotext.Get("unhandled operation")) @@ -219,19 +219,19 @@ func getFilter(cmdArgs *parser.Arguments) (upgrade.Filter, error) { }, nil } -func handleQuery(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments, dbExecutor db.Executor) error { +func handleQuery(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error { if cmdArgs.ExistsArg("u", "upgrades") { filter, err := getFilter(cmdArgs) if err != nil { return err } - return printUpdateList(ctx, cfg, cmdArgs, dbExecutor, + return printUpdateList(ctx, run, cmdArgs, dbExecutor, cmdArgs.ExistsDouble("u", "sysupgrade"), filter) } - if err := cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx, - cmdArgs, cfg.Mode, settings.NoConfirm)); err != nil { + if err := run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, + cmdArgs, run.Cfg.Mode, settings.NoConfirm)); err != nil { if str := err.Error(); strings.Contains(str, "exit status") { // yay -Qdt should not output anything in case of error return fmt.Errorf("") @@ -243,138 +243,139 @@ func handleQuery(ctx context.Context, cfg *settings.Configuration, cmdArgs *pars return nil } -func handleHelp(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments) error { - usage() +func handleHelp(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments) error { + usage(run.Logger) switch cmdArgs.Op { case "Y", "yay", "G", "getpkgbuild", "P", "show", "W", "web", "B", "build": return nil } - cfg.Runtime.Logger.Println("\npacman operation specific options:") - return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx, - cmdArgs, cfg.Mode, settings.NoConfirm)) + run.Logger.Println("\npacman operation specific options:") + return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, + cmdArgs, run.Cfg.Mode, settings.NoConfirm)) } -func handleVersion() { - fmt.Printf("yay v%s - libalpm v%s\n", yayVersion, alpm.Version()) +func handleVersion(logger *text.Logger) { + logger.Printf("yay v%s - libalpm v%s\n", yayVersion, alpm.Version()) } -func handlePrint(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments, dbExecutor db.Executor) error { +func handlePrint(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error { switch { case cmdArgs.ExistsArg("d", "defaultconfig"): tmpConfig := settings.DefaultConfig(yayVersion) - fmt.Printf("%v", tmpConfig) + run.Logger.Printf("%v", tmpConfig) return nil case cmdArgs.ExistsArg("g", "currentconfig"): - fmt.Printf("%v", cfg) + run.Logger.Printf("%v", run.Cfg) return nil case cmdArgs.ExistsArg("w", "news"): double := cmdArgs.ExistsDouble("w", "news") quiet := cmdArgs.ExistsArg("q", "quiet") - return news.PrintNewsFeed(ctx, cfg.Runtime.HTTPClient, dbExecutor.LastBuildTime(), cfg.BottomUp, double, quiet) + return news.PrintNewsFeed(ctx, run.HTTPClient, run.Logger, + dbExecutor.LastBuildTime(), run.Cfg.BottomUp, double, quiet) case cmdArgs.ExistsArg("c", "complete"): - return completion.Show(ctx, cfg.Runtime.HTTPClient, dbExecutor, - cfg.AURURL, cfg.CompletionPath, cfg.CompletionInterval, cmdArgs.ExistsDouble("c", "complete")) + return completion.Show(ctx, run.HTTPClient, dbExecutor, + run.Cfg.AURURL, run.Cfg.CompletionPath, run.Cfg.CompletionInterval, cmdArgs.ExistsDouble("c", "complete")) case cmdArgs.ExistsArg("s", "stats"): - return localStatistics(ctx, cfg, dbExecutor) + return localStatistics(ctx, run, dbExecutor) } return nil } -func handleYay(ctx context.Context, cfg *settings.Configuration, +func handleYay(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, cmdBuilder exe.ICmdBuilder, dbExecutor db.Executor, queryBuilder query.Builder, ) error { switch { case cmdArgs.ExistsArg("gendb"): - return createDevelDB(ctx, cfg, dbExecutor) + return createDevelDB(ctx, run, dbExecutor) case cmdArgs.ExistsDouble("c"): - return cleanDependencies(ctx, cfg, cmdBuilder, cmdArgs, dbExecutor, true) + return cleanDependencies(ctx, run.Cfg, cmdBuilder, cmdArgs, dbExecutor, true) case cmdArgs.ExistsArg("c", "clean"): - return cleanDependencies(ctx, cfg, cmdBuilder, cmdArgs, dbExecutor, false) + return cleanDependencies(ctx, run.Cfg, cmdBuilder, cmdArgs, dbExecutor, false) case len(cmdArgs.Targets) > 0: - return displayNumberMenu(ctx, cfg, cmdArgs.Targets, dbExecutor, queryBuilder, cmdArgs) + return displayNumberMenu(ctx, run, cmdArgs.Targets, dbExecutor, queryBuilder, cmdArgs) } return nil } -func handleWeb(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments) error { +func handleWeb(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments) error { switch { case cmdArgs.ExistsArg("v", "vote"): - return handlePackageVote(ctx, cmdArgs.Targets, cfg.Runtime.AURClient, - cfg.Runtime.VoteClient, true) + return handlePackageVote(ctx, cmdArgs.Targets, run.AURClient, run.Logger, + run.VoteClient, true) case cmdArgs.ExistsArg("u", "unvote"): - return handlePackageVote(ctx, cmdArgs.Targets, cfg.Runtime.AURClient, - cfg.Runtime.VoteClient, false) + return handlePackageVote(ctx, cmdArgs.Targets, run.AURClient, run.Logger, + run.VoteClient, false) } return nil } -func handleGetpkgbuild(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments, dbExecutor download.DBSearcher) error { +func handleGetpkgbuild(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor download.DBSearcher) error { if cmdArgs.ExistsArg("p", "print") { - return printPkgbuilds(dbExecutor, cfg.Runtime.AURClient, - cfg.Runtime.HTTPClient, cmdArgs.Targets, cfg.Mode, cfg.AURURL) + return printPkgbuilds(dbExecutor, run.AURClient, + run.HTTPClient, run.Logger, cmdArgs.Targets, run.Cfg.Mode, run.Cfg.AURURL) } - return getPkgbuilds(ctx, dbExecutor, cfg.Runtime.AURClient, cfg, + return getPkgbuilds(ctx, dbExecutor, run.AURClient, run, cmdArgs.Targets, cmdArgs.ExistsArg("f", "force")) } func handleUpgrade(ctx context.Context, - config *settings.Configuration, cmdArgs *parser.Arguments, + run *runtime.Runtime, cmdArgs *parser.Arguments, ) error { - return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx, - cmdArgs, config.Mode, settings.NoConfirm)) + return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, + cmdArgs, run.Cfg.Mode, settings.NoConfirm)) } // -B* options func handleBuild(ctx context.Context, - config *settings.Configuration, dbExecutor db.Executor, cmdArgs *parser.Arguments, + run *runtime.Runtime, dbExecutor db.Executor, cmdArgs *parser.Arguments, ) error { if cmdArgs.ExistsArg("i", "install") { - return installLocalPKGBUILD(ctx, config, cmdArgs, dbExecutor) + return installLocalPKGBUILD(ctx, run, cmdArgs, dbExecutor) } return nil } -func handleSync(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments, dbExecutor db.Executor) error { +func handleSync(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error { targets := cmdArgs.Targets switch { case cmdArgs.ExistsArg("s", "search"): - return syncSearch(ctx, targets, dbExecutor, cfg.Runtime.QueryBuilder, !cmdArgs.ExistsArg("q", "quiet")) + return syncSearch(ctx, targets, dbExecutor, run.QueryBuilder, !cmdArgs.ExistsArg("q", "quiet")) case cmdArgs.ExistsArg("p", "print", "print-format"): - return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx, - cmdArgs, cfg.Mode, settings.NoConfirm)) + return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, + cmdArgs, run.Cfg.Mode, settings.NoConfirm)) case cmdArgs.ExistsArg("c", "clean"): - return syncClean(ctx, cfg, cmdArgs, dbExecutor) + return syncClean(ctx, run, cmdArgs, dbExecutor) case cmdArgs.ExistsArg("l", "list"): - return syncList(ctx, cfg, cfg.Runtime.HTTPClient, cmdArgs, dbExecutor) + return syncList(ctx, run, run.HTTPClient, cmdArgs, dbExecutor) case cmdArgs.ExistsArg("g", "groups"): - return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx, - cmdArgs, cfg.Mode, settings.NoConfirm)) + return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, + cmdArgs, run.Cfg.Mode, settings.NoConfirm)) case cmdArgs.ExistsArg("i", "info"): - return syncInfo(ctx, cfg, cmdArgs, targets, dbExecutor) + return syncInfo(ctx, run, cmdArgs, targets, dbExecutor) case cmdArgs.ExistsArg("u", "sysupgrade") || len(cmdArgs.Targets) > 0: - return syncInstall(ctx, cfg, cmdArgs, dbExecutor) + return syncInstall(ctx, run, cmdArgs, dbExecutor) case cmdArgs.ExistsArg("y", "refresh"): - return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx, - cmdArgs, cfg.Mode, settings.NoConfirm)) + return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, + cmdArgs, run.Cfg.Mode, settings.NoConfirm)) } return nil } -func handleRemove(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments, localCache vcs.Store) error { - err := cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx, - cmdArgs, cfg.Mode, settings.NoConfirm)) +func handleRemove(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, localCache vcs.Store) error { + err := run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, + cmdArgs, run.Cfg.Mode, settings.NoConfirm)) if err == nil { localCache.RemovePackages(cmdArgs.Targets) } @@ -383,7 +384,7 @@ func handleRemove(ctx context.Context, cfg *settings.Configuration, cmdArgs *par } // NumberMenu presents a CLI for selecting packages to install. -func displayNumberMenu(ctx context.Context, cfg *settings.Configuration, pkgS []string, dbExecutor db.Executor, +func displayNumberMenu(ctx context.Context, run *runtime.Runtime, pkgS []string, dbExecutor db.Executor, queryBuilder query.Builder, cmdArgs *parser.Arguments, ) error { queryBuilder.Execute(ctx, dbExecutor, pkgS) @@ -397,9 +398,9 @@ func displayNumberMenu(ctx context.Context, cfg *settings.Configuration, pkgS [] return nil } - cfg.Runtime.Logger.Infoln(gotext.Get("Packages to install (eg: 1 2 3, 1-3 or ^4)")) + run.Logger.Infoln(gotext.Get("Packages to install (eg: 1 2 3, 1-3 or ^4)")) - numberBuf, err := cfg.Runtime.Logger.GetInput("", false) + numberBuf, err := run.Logger.GetInput("", false) if err != nil { return err } @@ -415,27 +416,27 @@ func displayNumberMenu(ctx context.Context, cfg *settings.Configuration, pkgS [] cmdArgs.Targets = targets if len(cmdArgs.Targets) == 0 { - fmt.Println(gotext.Get(" there is nothing to do")) + run.Logger.Println(gotext.Get(" there is nothing to do")) return nil } - return syncInstall(ctx, cfg, cmdArgs, dbExecutor) + return syncInstall(ctx, run, cmdArgs, dbExecutor) } -func syncList(ctx context.Context, cfg *settings.Configuration, +func syncList(ctx context.Context, run *runtime.Runtime, httpClient *http.Client, cmdArgs *parser.Arguments, dbExecutor db.Executor, ) error { aur := false for i := len(cmdArgs.Targets) - 1; i >= 0; i-- { - if cmdArgs.Targets[i] == "aur" && cfg.Mode.AtLeastAUR() { + if cmdArgs.Targets[i] == "aur" && run.Cfg.Mode.AtLeastAUR() { cmdArgs.Targets = append(cmdArgs.Targets[:i], cmdArgs.Targets[i+1:]...) aur = true } } - if cfg.Mode.AtLeastAUR() && (len(cmdArgs.Targets) == 0 || aur) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, cfg.AURURL+"/packages.gz", http.NoBody) + if run.Cfg.Mode.AtLeastAUR() && (len(cmdArgs.Targets) == 0 || aur) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, run.Cfg.AURURL+"/packages.gz", http.NoBody) if err != nil { return err } @@ -453,22 +454,22 @@ func syncList(ctx context.Context, cfg *settings.Configuration, for scanner.Scan() { name := scanner.Text() if cmdArgs.ExistsArg("q", "quiet") { - fmt.Println(name) + run.Logger.Println(name) } else { - fmt.Printf("%s %s %s", text.Magenta("aur"), text.Bold(name), text.Bold(text.Green(gotext.Get("unknown-version")))) + run.Logger.Printf("%s %s %s", text.Magenta("aur"), text.Bold(name), text.Bold(text.Green(gotext.Get("unknown-version")))) if dbExecutor.LocalPackage(name) != nil { - fmt.Print(text.Bold(text.Blue(gotext.Get(" [Installed]")))) + run.Logger.Print(text.Bold(text.Blue(gotext.Get(" [Installed]")))) } - fmt.Println() + run.Logger.Println() } } } - if cfg.Mode.AtLeastRepo() && (len(cmdArgs.Targets) != 0 || !aur) { - return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx, - cmdArgs, cfg.Mode, settings.NoConfirm)) + if run.Cfg.Mode.AtLeastRepo() && (len(cmdArgs.Targets) != 0 || !aur) { + return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, + cmdArgs, run.Cfg.Mode, settings.NoConfirm)) } return nil diff --git a/cmd_test.go b/cmd_test.go index 58e2457..5f3a3cd 100644 --- a/cmd_test.go +++ b/cmd_test.go @@ -19,6 +19,7 @@ import ( "github.com/Jguer/yay/v12/pkg/db/mock" mockaur "github.com/Jguer/yay/v12/pkg/dep/mock" "github.com/Jguer/yay/v12/pkg/query" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/parser" @@ -103,19 +104,19 @@ func TestYogurtMenuAURDB(t *testing.T) { }, } logger := text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test") - cfg := &settings.Configuration{ - RemoveMake: "no", - Runtime: &settings.Runtime{ - Logger: logger, - CmdBuilder: cmdBuilder, - VCSStore: &vcs.Mock{}, - QueryBuilder: query.NewSourceQueryBuilder(aurCache, logger, "votes", parser.ModeAny, "name", - true, false, true), - AURClient: aurCache, - }, - } - err = handleCmd(context.Background(), cfg, cmdArgs, db) + run := &runtime.Runtime{ + Cfg: &settings.Configuration{ + RemoveMake: "no", + }, + Logger: logger, + CmdBuilder: cmdBuilder, + VCSStore: &vcs.Mock{}, + QueryBuilder: query.NewSourceQueryBuilder(aurCache, logger, "votes", parser.ModeAny, "name", + true, false, true), + AURClient: aurCache, + } + err = handleCmd(context.Background(), run, cmdArgs, db) require.NoError(t, err) wantCapture := []string{} diff --git a/errors.go b/errors.go index 4e51797..1644733 100644 --- a/errors.go +++ b/errors.go @@ -7,56 +7,3 @@ import ( ) var ErrPackagesNotFound = errors.New(gotext.Get("could not find all required packages")) - -type NoPkgDestsFoundError struct { - dir string -} - -func (e *NoPkgDestsFoundError) Error() string { - return gotext.Get("could not find any package archives listed in %s", e.dir) -} - -type SetPkgReasonError struct { - exp bool // explicit -} - -func (e *SetPkgReasonError) Error() string { - reason := gotext.Get("explicit") - if !e.exp { - reason = gotext.Get("dependency") - } - - return gotext.Get("error updating package install reason to %s", reason) -} - -type FindPkgDestError struct { - name, pkgDest string -} - -func (e *FindPkgDestError) Error() string { - return gotext.Get( - "the PKGDEST for %s is listed by makepkg but does not exist: %s", - e.name, e.pkgDest) -} - -type PkgDestNotInListError struct { - name string -} - -func (e *PkgDestNotInListError) Error() string { - return gotext.Get("could not find PKGDEST for: %s", e.name) -} - -type FailedIgnoredPkgError struct { - pkgErrors map[string]error -} - -func (e *FailedIgnoredPkgError) Error() string { - msg := gotext.Get("Failed to install the following packages. Manual intervention is required:") - - for pkg, err := range e.pkgErrors { - msg += "\n" + pkg + " - " + err.Error() - } - - return msg -} diff --git a/get.go b/get.go index 783f62c..c1a4173 100644 --- a/get.go +++ b/get.go @@ -11,24 +11,24 @@ import ( "github.com/leonelquinteros/gotext" "github.com/Jguer/yay/v12/pkg/download" - "github.com/Jguer/yay/v12/pkg/settings" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/text" ) // yay -Gp. -func printPkgbuilds(dbExecutor download.DBSearcher, aurClient aur.QueryClient, httpClient *http.Client, targets []string, +func printPkgbuilds(dbExecutor download.DBSearcher, aurClient aur.QueryClient, + httpClient *http.Client, logger *text.Logger, targets []string, mode parser.TargetMode, aurURL string, ) error { - pkgbuilds, err := download.PKGBUILDs(dbExecutor, aurClient, httpClient, targets, aurURL, mode) + pkgbuilds, err := download.PKGBUILDs(dbExecutor, aurClient, httpClient, logger, targets, aurURL, mode) if err != nil { - text.Errorln(err) + logger.Errorln(err) } if len(pkgbuilds) != 0 { for target, pkgbuild := range pkgbuilds { - fmt.Printf("\n\n# %s\n\n", target) - fmt.Print(string(pkgbuild)) + logger.Printf("\n\n# %s\n\n%s", target, string(pkgbuild)) } } @@ -41,7 +41,7 @@ func printPkgbuilds(dbExecutor download.DBSearcher, aurClient aur.QueryClient, h } } - text.Warnln(gotext.Get("Unable to find the following packages:"), " ", strings.Join(missing, ", ")) + logger.Warnln(gotext.Get("Unable to find the following packages:"), " ", strings.Join(missing, ", ")) return fmt.Errorf("") } @@ -51,7 +51,7 @@ func printPkgbuilds(dbExecutor download.DBSearcher, aurClient aur.QueryClient, h // yay -G. func getPkgbuilds(ctx context.Context, dbExecutor download.DBSearcher, aurClient aur.QueryClient, - config *settings.Configuration, targets []string, force bool, + run *runtime.Runtime, targets []string, force bool, ) error { wd, err := os.Getwd() if err != nil { @@ -59,9 +59,9 @@ func getPkgbuilds(ctx context.Context, dbExecutor download.DBSearcher, aurClient } cloned, errD := download.PKGBUILDRepos(ctx, dbExecutor, aurClient, - config.Runtime.CmdBuilder, targets, config.Mode, config.AURURL, wd, force) + run.CmdBuilder, run.Logger, targets, run.Cfg.Mode, run.Cfg.AURURL, wd, force) if errD != nil { - text.Errorln(errD) + run.Logger.Errorln(errD) } if len(targets) != len(cloned) { @@ -73,7 +73,7 @@ func getPkgbuilds(ctx context.Context, dbExecutor download.DBSearcher, aurClient } } - text.Warnln(gotext.Get("Unable to find the following packages:"), " ", strings.Join(missing, ", ")) + run.Logger.Warnln(gotext.Get("Unable to find the following packages:"), " ", strings.Join(missing, ", ")) err = fmt.Errorf("") } diff --git a/local_install.go b/local_install.go index a19a701..d940cc8 100644 --- a/local_install.go +++ b/local_install.go @@ -12,19 +12,18 @@ import ( "github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/dep" "github.com/Jguer/yay/v12/pkg/multierror" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/parser" + "github.com/Jguer/yay/v12/pkg/sync" gosrc "github.com/Morganamilo/go-srcinfo" "github.com/leonelquinteros/gotext" "github.com/pkg/errors" ) -var ( - ErrInstallRepoPkgs = errors.New(gotext.Get("error installing repo packages")) - ErrNoBuildFiles = errors.New(gotext.Get("cannot find PKGBUILD and .SRCINFO in directory")) -) +var ErrNoBuildFiles = errors.New(gotext.Get("cannot find PKGBUILD and .SRCINFO in directory")) func srcinfoExists(ctx context.Context, cmdBuilder exe.ICmdBuilder, targetDir string, @@ -56,12 +55,12 @@ func srcinfoExists(ctx context.Context, func installLocalPKGBUILD( ctx context.Context, - config *settings.Configuration, + run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor, ) error { - aurCache := config.Runtime.AURClient - noCheck := strings.Contains(config.MFlags, "--nocheck") + aurCache := run.AURClient + noCheck := strings.Contains(run.Cfg.MFlags, "--nocheck") if len(cmdArgs.Targets) < 1 { return errors.New(gotext.Get("no target directories specified")) @@ -69,7 +68,7 @@ func installLocalPKGBUILD( srcInfos := map[string]*gosrc.Srcinfo{} for _, targetDir := range cmdArgs.Targets { - if err := srcinfoExists(ctx, config.Runtime.CmdBuilder, targetDir); err != nil { + if err := srcinfoExists(ctx, run.CmdBuilder, targetDir); err != nil { return err } @@ -83,13 +82,13 @@ func installLocalPKGBUILD( grapher := dep.NewGrapher(dbExecutor, aurCache, false, settings.NoConfirm, cmdArgs.ExistsDouble("d", "nodeps"), noCheck, cmdArgs.ExistsArg("needed"), - config.Runtime.Logger.Child("grapher")) + run.Logger.Child("grapher")) graph, err := grapher.GraphFromSrcInfos(ctx, nil, srcInfos) if err != nil { return err } - opService := NewOperationService(ctx, config, dbExecutor) + opService := sync.NewOperationService(ctx, dbExecutor, run) multiErr := &multierror.MultiError{} targets := graph.TopoSortedLayerMap(func(name string, ii *dep.InstallInfo) error { if ii.Source == dep.Missing { @@ -101,5 +100,5 @@ func installLocalPKGBUILD( if err := multiErr.Return(); err != nil { return err } - return opService.Run(ctx, cmdArgs, targets, []string{}) + return opService.Run(ctx, run, cmdArgs, targets, []string{}) } diff --git a/local_install_test.go b/local_install_test.go index 480b65c..9cf7054 100644 --- a/local_install_test.go +++ b/local_install_test.go @@ -6,6 +6,7 @@ package main import ( "context" "fmt" + "io" "os" "os/exec" "path/filepath" @@ -19,12 +20,18 @@ import ( "github.com/Jguer/yay/v12/pkg/db/mock" mockaur "github.com/Jguer/yay/v12/pkg/dep/mock" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/parser" + "github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/vcs" ) +func newTestLogger() *text.Logger { + return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test") +} + func TestIntegrationLocalInstall(t *testing.T) { makepkgBin := t.TempDir() + "/makepkg" pacmanBin := t.TempDir() + "/pacman" @@ -142,21 +149,21 @@ func TestIntegrationLocalInstall(t *testing.T) { InstalledRemotePackageNamesFn: func() []string { return []string{} }, } - config := &settings.Configuration{ - RemoveMake: "no", - Runtime: &settings.Runtime{ - Logger: NewTestLogger(), - CmdBuilder: cmdBuilder, - VCSStore: &vcs.Mock{}, - AURClient: &mockaur.MockAUR{ - GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { - return []aur.Pkg{}, nil - }, + run := &runtime.Runtime{ + Cfg: &settings.Configuration{ + RemoveMake: "no", + }, + Logger: newTestLogger(), + CmdBuilder: cmdBuilder, + VCSStore: &vcs.Mock{}, + AURClient: &mockaur.MockAUR{ + GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { + return []aur.Pkg{}, nil }, }, } - err = handleCmd(context.Background(), config, cmdArgs, db) + err = handleCmd(context.Background(), run, cmdArgs, db) require.NoError(t, err) require.Len(t, mockRunner.ShowCalls, len(wantShow)) @@ -263,20 +270,19 @@ func TestIntegrationLocalInstallMissingDep(t *testing.T) { LocalPackageFn: func(string) mock.IPackage { return nil }, } - config := &settings.Configuration{ - Runtime: &settings.Runtime{ - Logger: NewTestLogger(), - CmdBuilder: cmdBuilder, - VCSStore: &vcs.Mock{}, - AURClient: &mockaur.MockAUR{ - GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { - return []aur.Pkg{}, nil - }, + run := &runtime.Runtime{ + Cfg: &settings.Configuration{}, + Logger: newTestLogger(), + CmdBuilder: cmdBuilder, + VCSStore: &vcs.Mock{}, + AURClient: &mockaur.MockAUR{ + GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { + return []aur.Pkg{}, nil }, }, } - err = handleCmd(context.Background(), config, cmdArgs, db) + err = handleCmd(context.Background(), run, cmdArgs, db) require.ErrorContains(t, err, wantErr.Error()) require.Len(t, mockRunner.ShowCalls, len(wantShow)) @@ -421,21 +427,21 @@ func TestIntegrationLocalInstallNeeded(t *testing.T) { InstalledRemotePackageNamesFn: func() []string { return []string{} }, } - config := &settings.Configuration{ - RemoveMake: "no", - Runtime: &settings.Runtime{ - Logger: NewTestLogger(), - CmdBuilder: cmdBuilder, - VCSStore: &vcs.Mock{}, - AURClient: &mockaur.MockAUR{ - GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { - return []aur.Pkg{}, nil - }, + run := &runtime.Runtime{ + Cfg: &settings.Configuration{ + RemoveMake: "no", + }, + Logger: newTestLogger(), + CmdBuilder: cmdBuilder, + VCSStore: &vcs.Mock{}, + AURClient: &mockaur.MockAUR{ + GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { + return []aur.Pkg{}, nil }, }, } - err = handleCmd(context.Background(), config, cmdArgs, db) + err = handleCmd(context.Background(), run, cmdArgs, db) require.NoError(t, err) require.Len(t, mockRunner.ShowCalls, len(wantShow), "show calls: %v", mockRunner.ShowCalls) @@ -585,22 +591,22 @@ func TestIntegrationLocalInstallGenerateSRCINFO(t *testing.T) { InstalledRemotePackageNamesFn: func() []string { return []string{} }, } - config := &settings.Configuration{ - RemoveMake: "no", - Debug: false, - Runtime: &settings.Runtime{ - Logger: NewTestLogger(), - CmdBuilder: cmdBuilder, - VCSStore: &vcs.Mock{}, - AURClient: &mockaur.MockAUR{ - GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { - return []aur.Pkg{}, nil - }, + run := &runtime.Runtime{ + Cfg: &settings.Configuration{ + RemoveMake: "no", + Debug: false, + }, + Logger: newTestLogger(), + CmdBuilder: cmdBuilder, + VCSStore: &vcs.Mock{}, + AURClient: &mockaur.MockAUR{ + GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { + return []aur.Pkg{}, nil }, }, } - err = handleCmd(context.Background(), config, cmdArgs, db) + err = handleCmd(context.Background(), run, cmdArgs, db) require.NoError(t, err) require.Len(t, mockRunner.ShowCalls, len(wantShow)) @@ -651,7 +657,6 @@ func TestIntegrationLocalInstallMissingFiles(t *testing.T) { wantCapture := []string{} captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) { - fmt.Println(cmd.Args) if cmd.Args[1] == "--printsrcinfo" { return string(srcinfo), "", nil } @@ -722,16 +727,17 @@ func TestIntegrationLocalInstallMissingFiles(t *testing.T) { }, } - config := &settings.Configuration{ - RemoveMake: "no", - Runtime: &settings.Runtime{ - Logger: NewTestLogger(), - CmdBuilder: cmdBuilder, - VCSStore: &vcs.Mock{}, - AURClient: &mockaur.MockAUR{ - GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { - return []aur.Pkg{}, nil - }, + config := &runtime.Runtime{ + Cfg: &settings.Configuration{ + RemoveMake: "no", + Debug: false, + }, + Logger: newTestLogger(), + CmdBuilder: cmdBuilder, + VCSStore: &vcs.Mock{}, + AURClient: &mockaur.MockAUR{ + GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { + return []aur.Pkg{}, nil }, }, } @@ -848,16 +854,16 @@ func TestIntegrationLocalInstallWithDepsProvides(t *testing.T) { InstalledRemotePackageNamesFn: func() []string { return []string{} }, } - config := &settings.Configuration{ - RemoveMake: "no", - Runtime: &settings.Runtime{ - Logger: NewTestLogger(), - CmdBuilder: cmdBuilder, - VCSStore: &vcs.Mock{}, - AURClient: &mockaur.MockAUR{ - GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { - return []aur.Pkg{}, nil - }, + config := &runtime.Runtime{ + Cfg: &settings.Configuration{ + RemoveMake: "no", + }, + Logger: newTestLogger(), + CmdBuilder: cmdBuilder, + VCSStore: &vcs.Mock{}, + AURClient: &mockaur.MockAUR{ + GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { + return []aur.Pkg{}, nil }, }, } @@ -988,21 +994,21 @@ func TestIntegrationLocalInstallTwoSrcInfosWithDeps(t *testing.T) { InstalledRemotePackageNamesFn: func() []string { return []string{} }, } - config := &settings.Configuration{ - RemoveMake: "no", - Runtime: &settings.Runtime{ - Logger: NewTestLogger(), - CmdBuilder: cmdBuilder, - VCSStore: &vcs.Mock{}, - AURClient: &mockaur.MockAUR{ - GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { - return []aur.Pkg{}, nil - }, + run := &runtime.Runtime{ + Cfg: &settings.Configuration{ + RemoveMake: "no", + }, + Logger: newTestLogger(), + CmdBuilder: cmdBuilder, + VCSStore: &vcs.Mock{}, + AURClient: &mockaur.MockAUR{ + GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { + return []aur.Pkg{}, nil }, }, } - err = handleCmd(context.Background(), config, cmdArgs, db) + err = handleCmd(context.Background(), run, cmdArgs, db) require.NoError(t, err) require.Len(t, mockRunner.ShowCalls, len(wantShow)) diff --git a/main.go b/main.go index 2c42027..d18cf4a 100644 --- a/main.go +++ b/main.go @@ -9,9 +9,8 @@ import ( "github.com/leonelquinteros/gotext" - "github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/db/ialpm" - "github.com/Jguer/yay/v12/pkg/query" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/text" @@ -39,6 +38,7 @@ func initGotext() { } func main() { + fallbackLog := text.NewLogger(os.Stdout, os.Stderr, os.Stdin, false, "fallback") var ( err error ctx = context.Background() @@ -47,7 +47,7 @@ func main() { defer func() { if rec := recover(); rec != nil { - text.Errorln(rec) + fallbackLog.Errorln(rec) debug.PrintStack() } @@ -57,15 +57,15 @@ func main() { initGotext() if os.Geteuid() == 0 { - text.Warnln(gotext.Get("Avoid running yay as root/sudo.")) + fallbackLog.Warnln(gotext.Get("Avoid running yay as root/sudo.")) } configPath := settings.GetConfigPath() // Parse config - cfg, err := settings.NewConfig(configPath, yayVersion) + cfg, err := settings.NewConfig(fallbackLog, configPath, yayVersion) if err != nil { if str := err.Error(); str != "" { - text.Errorln(str) + fallbackLog.Errorln(str) } ret = 1 @@ -73,13 +73,9 @@ func main() { return } - if cfg.Debug { - text.GlobalLogger.Debug = true - } - - if errS := cfg.RunMigrations( + if errS := cfg.RunMigrations(fallbackLog, settings.DefaultMigrations(), configPath, yayVersion); errS != nil { - text.Errorln(errS) + fallbackLog.Errorln(errS) } cmdArgs := parser.MakeArguments() @@ -87,7 +83,7 @@ func main() { // Parse command line if err = cfg.ParseCommandLine(cmdArgs); err != nil { if str := err.Error(); str != "" { - text.Errorln(str) + fallbackLog.Errorln(str) } ret = 1 @@ -97,15 +93,15 @@ func main() { if cfg.SaveConfig { if errS := cfg.Save(configPath, yayVersion); errS != nil { - text.Errorln(errS) + fallbackLog.Errorln(errS) } } - // Build runtime - runtime, err := settings.BuildRuntime(cfg, cmdArgs, yayVersion) + // Build run + run, err := runtime.NewRuntime(cfg, cmdArgs, yayVersion) if err != nil { if str := err.Error(); str != "" { - text.Errorln(str) + fallbackLog.Errorln(str) } ret = 1 @@ -113,35 +109,10 @@ func main() { return } - cfg.Runtime = runtime - - cfg.Runtime.QueryBuilder = query.NewSourceQueryBuilder( - cfg.Runtime.AURClient, - cfg.Runtime.Logger.Child("mixed.querybuilder"), cfg.SortBy, - cfg.Mode, cfg.SearchBy, - cfg.BottomUp, cfg.SingleLineResults, cfg.SeparateSources) - - var useColor bool - - cfg.Runtime.PacmanConf, useColor, err = settings.RetrievePacmanConfig(cmdArgs, cfg.PacmanConf) + dbExecutor, err := ialpm.NewExecutor(run.PacmanConf, run.Logger.Child("db")) if err != nil { if str := err.Error(); str != "" { - text.Errorln(str) - } - - ret = 1 - - return - } - - cfg.Runtime.CmdBuilder.SetPacmanDBPath(cfg.Runtime.PacmanConf.DBPath) - - text.UseColor = useColor - - dbExecutor, err := ialpm.NewExecutor(cfg.Runtime.PacmanConf, runtime.Logger.Child("db")) - if err != nil { - if str := err.Error(); str != "" { - text.Errorln(str) + fallbackLog.Errorln(str) } ret = 1 @@ -151,19 +122,18 @@ func main() { defer func() { if rec := recover(); rec != nil { - text.Errorln(rec) - debug.PrintStack() + fallbackLog.Errorln(rec, string(debug.Stack())) } dbExecutor.Cleanup() }() - if err = handleCmd(ctx, cfg, cmdArgs, db.Executor(dbExecutor)); err != nil { + if err = handleCmd(ctx, run, cmdArgs, dbExecutor); err != nil { if str := err.Error(); str != "" { - text.Errorln(str) + fallbackLog.Errorln(str) if cmdArgs.ExistsArg("c") && cmdArgs.ExistsArg("y") && cmdArgs.Op == "S" { // Remove after 2023-10-01 - text.Errorln("Did you mean 'yay -Yc'?") + fallbackLog.Errorln("Did you mean 'yay -Yc'?") } } diff --git a/pkg/cmd/graph/main.go b/pkg/cmd/graph/main.go index 991049e..410382b 100644 --- a/pkg/cmd/graph/main.go +++ b/pkg/cmd/graph/main.go @@ -8,6 +8,7 @@ import ( "github.com/Jguer/yay/v12/pkg/db/ialpm" "github.com/Jguer/yay/v12/pkg/dep" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/text" @@ -17,44 +18,45 @@ import ( "github.com/pkg/errors" ) -func handleCmd() error { - config, err := settings.NewConfig(settings.GetConfigPath(), "") +func handleCmd(logger *text.Logger) error { + cfg, err := settings.NewConfig(logger, settings.GetConfigPath(), "") if err != nil { return err } cmdArgs := parser.MakeArguments() - if errP := config.ParseCommandLine(cmdArgs); errP != nil { + if errP := cfg.ParseCommandLine(cmdArgs); errP != nil { return errP } - pacmanConf, _, err := settings.RetrievePacmanConfig(cmdArgs, config.PacmanConf) + run, err := runtime.NewRuntime(cfg, cmdArgs, "1.0.0") if err != nil { return err } - dbExecutor, err := ialpm.NewExecutor(pacmanConf, text.GlobalLogger) + dbExecutor, err := ialpm.NewExecutor(run.PacmanConf, logger) if err != nil { return err } aurCache, err := metadata.New( metadata.WithCacheFilePath( - filepath.Join(config.BuildDir, "aur.json"))) + filepath.Join(cfg.BuildDir, "aur.json"))) if err != nil { return errors.Wrap(err, gotext.Get("failed to retrieve aur Cache")) } grapher := dep.NewGrapher(dbExecutor, aurCache, true, settings.NoConfirm, cmdArgs.ExistsDouble("d", "nodeps"), false, false, - config.Runtime.Logger.Child("grapher")) + run.Logger.Child("grapher")) return graphPackage(context.Background(), grapher, cmdArgs.Targets) } func main() { - if err := handleCmd(); err != nil { - text.Errorln(err) + fallbackLog := text.NewLogger(os.Stdout, os.Stderr, os.Stdin, false, "fallback") + if err := handleCmd(fallbackLog); err != nil { + fallbackLog.Errorln(err) os.Exit(1) } } diff --git a/pkg/dep/topo/dep.go b/pkg/dep/topo/dep.go index 56b9c83..61e880f 100644 --- a/pkg/dep/topo/dep.go +++ b/pkg/dep/topo/dep.go @@ -5,8 +5,6 @@ import ( "strings" "github.com/Jguer/go-alpm/v2" - - "github.com/Jguer/yay/v12/pkg/text" ) type ( @@ -244,7 +242,6 @@ func (g *Graph[T, V]) Prune(node T) []T { // Remove edges from things that depend on `node`. for dependent := range g.dependents[node] { last := g.dependencies.removeFromDepmap(dependent, node) - text.Debugln("pruning dependent", dependent, last) if last { pruned = append(pruned, g.Prune(dependent)...) } @@ -255,7 +252,6 @@ func (g *Graph[T, V]) Prune(node T) []T { // Remove all edges from node to the things it depends on. for dependency := range g.dependencies[node] { last := g.dependents.removeFromDepmap(dependency, node) - text.Debugln("pruning dependency", dependency, last) if last { pruned = append(pruned, g.Prune(dependency)...) } diff --git a/pkg/download/aur.go b/pkg/download/aur.go index 0a45198..8650772 100644 --- a/pkg/download/aur.go +++ b/pkg/download/aur.go @@ -48,7 +48,7 @@ func AURPKGBUILDRepo(ctx context.Context, cmdBuilder exe.GitCmdBuilder, aurURL, func AURPKGBUILDRepos( ctx context.Context, - cmdBuilder exe.GitCmdBuilder, + cmdBuilder exe.GitCmdBuilder, logger *text.Logger, targets []string, aurURL, dest string, force bool, ) (map[string]bool, error) { cloned := make(map[string]bool, len(targets)) @@ -80,7 +80,7 @@ func AURPKGBUILDRepos( mux.Unlock() } - text.OperationInfoln( + logger.OperationInfoln( gotext.Get("(%d/%d) Downloaded PKGBUILD: %s", progress, len(targets), text.Cyan(target))) diff --git a/pkg/download/aur_test.go b/pkg/download/aur_test.go index aa61a11..d4803e0 100644 --- a/pkg/download/aur_test.go +++ b/pkg/download/aur_test.go @@ -158,7 +158,7 @@ func TestAURPKGBUILDRepos(t *testing.T) { GitFlags: []string{}, }, } - cloned, err := AURPKGBUILDRepos(context.Background(), cmdBuilder, targets, "https://aur.archlinux.org", dir, false) + cloned, err := AURPKGBUILDRepos(context.Background(), cmdBuilder, newTestLogger(), targets, "https://aur.archlinux.org", dir, false) assert.NoError(t, err) assert.EqualValues(t, map[string]bool{"yay": true, "yay-bin": false, "yay-git": true}, cloned) diff --git a/pkg/download/unified.go b/pkg/download/unified.go index 48fa5b8..3433b85 100644 --- a/pkg/download/unified.go +++ b/pkg/download/unified.go @@ -81,8 +81,8 @@ func getURLName(pkg db.IPackage) string { return name } -func PKGBUILDs(dbExecutor DBSearcher, aurClient aur.QueryClient, httpClient *http.Client, targets []string, - aurURL string, mode parser.TargetMode, +func PKGBUILDs(dbExecutor DBSearcher, aurClient aur.QueryClient, httpClient *http.Client, + logger *text.Logger, targets []string, aurURL string, mode parser.TargetMode, ) (map[string][]byte, error) { pkgbuilds := make(map[string][]byte, len(targets)) @@ -96,7 +96,7 @@ func PKGBUILDs(dbExecutor DBSearcher, aurClient aur.QueryClient, httpClient *htt for _, target := range targets { // Probably replaceable by something in query. - dbName, name, isAUR, toSkip := getPackageUsableName(dbExecutor, aurClient, target, mode) + dbName, name, isAUR, toSkip := getPackageUsableName(dbExecutor, aurClient, logger, target, mode) if toSkip { continue } @@ -136,7 +136,7 @@ func PKGBUILDs(dbExecutor DBSearcher, aurClient aur.QueryClient, httpClient *htt } func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.QueryClient, - cmdBuilder exe.GitCmdBuilder, + cmdBuilder exe.GitCmdBuilder, logger *text.Logger, targets []string, mode parser.TargetMode, aurURL, dest string, force bool, ) (map[string]bool, error) { cloned := make(map[string]bool, len(targets)) @@ -151,7 +151,7 @@ func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.Que for _, target := range targets { // Probably replaceable by something in query. - dbName, name, isAUR, toSkip := getPackageUsableName(dbExecutor, aurClient, target, mode) + dbName, name, isAUR, toSkip := getPackageUsableName(dbExecutor, aurClient, logger, target, mode) if toSkip { continue } @@ -184,11 +184,11 @@ func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.Que } if aur { - text.OperationInfoln( + logger.OperationInfoln( gotext.Get("(%d/%d) Downloaded PKGBUILD: %s", progress, len(targets), text.Cyan(pkgName))) } else { - text.OperationInfoln( + logger.OperationInfoln( gotext.Get("(%d/%d) Downloaded PKGBUILD from ABS: %s", progress, len(targets), text.Cyan(pkgName))) } @@ -206,7 +206,7 @@ func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.Que // TODO: replace with dep.ResolveTargets. func getPackageUsableName(dbExecutor DBSearcher, aurClient aur.QueryClient, - target string, mode parser.TargetMode, + logger *text.Logger, target string, mode parser.TargetMode, ) (dbname, pkgname string, isAUR, toSkip bool) { dbName, name := text.SplitDBFromName(target) if dbName != "aur" && mode.AtLeastRepo() { @@ -239,7 +239,7 @@ func getPackageUsableName(dbExecutor DBSearcher, aurClient aur.QueryClient, Needles: []string{name}, }) if err != nil { - text.Warnln(err) + logger.Warnln(err) return dbName, name, true, true } diff --git a/pkg/download/unified_test.go b/pkg/download/unified_test.go index b9e5f94..a8bc51f 100644 --- a/pkg/download/unified_test.go +++ b/pkg/download/unified_test.go @@ -5,6 +5,7 @@ package download import ( "context" + "io" "net/http" "os" "path/filepath" @@ -22,6 +23,10 @@ import ( "github.com/Jguer/yay/v12/pkg/text" ) +func newTestLogger() *text.Logger { + return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test") +} + // GIVEN 2 aur packages and 1 in repo // GIVEN package in repo is already present // WHEN defining package db as a target @@ -56,7 +61,7 @@ func TestPKGBUILDReposDefinedDBPull(t *testing.T) { absPackagesDB: map[string]string{"yay": "core"}, } cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient, - cmdBuilder, + cmdBuilder, newTestLogger(), targets, parser.ModeAny, "https://aur.archlinux.org", dir, false) assert.NoError(t, err) @@ -90,7 +95,7 @@ func TestPKGBUILDReposDefinedDBClone(t *testing.T) { absPackagesDB: map[string]string{"yay": "core"}, } cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient, - cmdBuilder, + cmdBuilder, newTestLogger(), targets, parser.ModeAny, "https://aur.archlinux.org", dir, false) assert.NoError(t, err) @@ -124,7 +129,7 @@ func TestPKGBUILDReposClone(t *testing.T) { absPackagesDB: map[string]string{"yay": "core"}, } cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient, - cmdBuilder, + cmdBuilder, newTestLogger(), targets, parser.ModeAny, "https://aur.archlinux.org", dir, false) assert.NoError(t, err) @@ -158,7 +163,7 @@ func TestPKGBUILDReposNotFound(t *testing.T) { absPackagesDB: map[string]string{"yay": "core"}, } cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient, - cmdBuilder, + cmdBuilder, newTestLogger(), targets, parser.ModeAny, "https://aur.archlinux.org", dir, false) assert.NoError(t, err) @@ -192,7 +197,7 @@ func TestPKGBUILDReposRepoMode(t *testing.T) { absPackagesDB: map[string]string{"yay": "core"}, } cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient, - cmdBuilder, + cmdBuilder, newTestLogger(), targets, parser.ModeRepo, "https://aur.archlinux.org", dir, false) assert.NoError(t, err) @@ -230,7 +235,7 @@ func TestPKGBUILDFull(t *testing.T) { absPackagesDB: map[string]string{"yay": "core"}, } - fetched, err := PKGBUILDs(searcher, mockClient, &http.Client{}, + fetched, err := PKGBUILDs(searcher, mockClient, &http.Client{}, newTestLogger(), targets, "https://aur.archlinux.org", parser.ModeAny) assert.NoError(t, err) @@ -268,7 +273,7 @@ func TestPKGBUILDReposMissingAUR(t *testing.T) { absPackagesDB: map[string]string{"yay": "core"}, } cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient, - cmdBuilder, + cmdBuilder, newTestLogger(), targets, parser.ModeAny, "https://aur.archlinux.org", dir, false) assert.NoError(t, err) diff --git a/pkg/menus/clean_menu.go b/pkg/menus/clean_menu.go index 703a7e7..4e10df3 100644 --- a/pkg/menus/clean_menu.go +++ b/pkg/menus/clean_menu.go @@ -9,6 +9,7 @@ import ( mapset "github.com/deckarep/golang-set/v2" "github.com/leonelquinteros/gotext" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/text" ) @@ -23,7 +24,7 @@ func anyExistInCache(pkgbuildDirs map[string]string) bool { return false } -func CleanFn(ctx context.Context, config *settings.Configuration, w io.Writer, +func CleanFn(ctx context.Context, run *runtime.Runtime, w io.Writer, pkgbuildDirsByBase map[string]string, installed mapset.Set[string], ) error { if len(pkgbuildDirsByBase) == 0 { @@ -49,25 +50,25 @@ func CleanFn(ctx context.Context, config *settings.Configuration, w io.Writer, bases = append(bases, pkg) } - toClean, errClean := selectionMenu(w, pkgbuildDirsByBase, bases, installed, + toClean, errClean := selectionMenu(run.Logger, pkgbuildDirsByBase, bases, installed, gotext.Get("Packages to cleanBuild?"), - settings.NoConfirm, config.AnswerClean, skipFunc) + settings.NoConfirm, run.Cfg.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))) + run.Logger.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", "origin/HEAD")); err != nil { - text.Warnln(gotext.Get("Unable to clean:"), dir) + if err := run.CmdBuilder.Show(run.CmdBuilder.BuildGitCmd(ctx, dir, "reset", "--hard", "origin/HEAD")); err != nil { + run.Logger.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) + if err := run.CmdBuilder.Show(run.CmdBuilder.BuildGitCmd(ctx, dir, "clean", "-fdx")); err != nil { + run.Logger.Warnln(gotext.Get("Unable to clean:"), dir) return err } diff --git a/pkg/menus/diff_menu.go b/pkg/menus/diff_menu.go index 1a2409e..6bb3da6 100644 --- a/pkg/menus/diff_menu.go +++ b/pkg/menus/diff_menu.go @@ -5,13 +5,13 @@ import ( "context" "fmt" "io" - "os" "strings" mapset "github.com/deckarep/golang-set/v2" "github.com/leonelquinteros/gotext" "github.com/Jguer/yay/v12/pkg/multierror" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/text" @@ -22,7 +22,7 @@ const ( gitDiffRefName = "AUR_SEEN" ) -func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, +func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, logger *text.Logger, pkgbuildDirs map[string]string, bases []string, ) error { var errMulti multierror.MultiError @@ -46,7 +46,7 @@ func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, } if !hasDiff { - text.Warnln(gotext.Get("%s: No changes -- skipping", text.Cyan(pkg))) + logger.Warnln(gotext.Get("%s: No changes -- skipping", text.Cyan(pkg))) continue } @@ -145,7 +145,7 @@ func updatePkgbuildSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgb return errMulti.Return() } -func DiffFn(ctx context.Context, config *settings.Configuration, w io.Writer, +func DiffFn(ctx context.Context, run *runtime.Runtime, w io.Writer, pkgbuildDirsByBase map[string]string, installed mapset.Set[string], ) error { if len(pkgbuildDirsByBase) == 0 { @@ -157,23 +157,23 @@ func DiffFn(ctx context.Context, config *settings.Configuration, w io.Writer, bases = append(bases, base) } - toDiff, errMenu := selectionMenu(w, pkgbuildDirsByBase, bases, installed, gotext.Get("Diffs to show?"), - settings.NoConfirm, config.AnswerDiff, nil) + toDiff, errMenu := selectionMenu(run.Logger, pkgbuildDirsByBase, bases, installed, gotext.Get("Diffs to show?"), + settings.NoConfirm, run.Cfg.AnswerDiff, nil) if errMenu != nil || len(toDiff) == 0 { return errMenu } - if errD := showPkgbuildDiffs(ctx, config.Runtime.CmdBuilder, pkgbuildDirsByBase, toDiff); errD != nil { + if errD := showPkgbuildDiffs(ctx, run.CmdBuilder, run.Logger, pkgbuildDirsByBase, toDiff); errD != nil { return errD } - fmt.Println() + run.Logger.Println() - if !text.ContinueTask(os.Stdin, gotext.Get("Proceed with install?"), true, false) { + if !run.Logger.ContinueTask(gotext.Get("Proceed with install?"), true, false) { return settings.ErrUserAbort{} } - if errUpd := updatePkgbuildSeenRef(ctx, config.Runtime.CmdBuilder, pkgbuildDirsByBase, toDiff); errUpd != nil { + if errUpd := updatePkgbuildSeenRef(ctx, run.CmdBuilder, pkgbuildDirsByBase, toDiff); errUpd != nil { return errUpd } diff --git a/pkg/menus/edit_menu.go b/pkg/menus/edit_menu.go index 95c5a30..7100e5b 100644 --- a/pkg/menus/edit_menu.go +++ b/pkg/menus/edit_menu.go @@ -14,6 +14,7 @@ import ( mapset "github.com/deckarep/golang-set/v2" "github.com/leonelquinteros/gotext" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/text" ) @@ -59,7 +60,7 @@ func editor(log *text.Logger, editorConfig, editorFlags string, noConfirm bool) for { log.Infoln(gotext.Get("Edit PKGBUILD with?")) - editorInput, err := text.GetInput(os.Stdin, "", noConfirm) + editorInput, err := log.GetInput("", noConfirm) if err != nil { log.Errorln(err) continue @@ -113,7 +114,7 @@ func editPkgbuilds(log *text.Logger, pkgbuildDirs map[string]string, bases []str return nil } -func EditFn(ctx context.Context, cfg *settings.Configuration, w io.Writer, +func EditFn(ctx context.Context, run *runtime.Runtime, w io.Writer, pkgbuildDirsByBase map[string]string, installed mapset.Set[string], ) error { if len(pkgbuildDirsByBase) == 0 { @@ -125,21 +126,21 @@ func EditFn(ctx context.Context, cfg *settings.Configuration, w io.Writer, bases = append(bases, pkg) } - toEdit, errMenu := selectionMenu(w, pkgbuildDirsByBase, bases, installed, - gotext.Get("PKGBUILDs to edit?"), settings.NoConfirm, cfg.AnswerEdit, nil) + toEdit, errMenu := selectionMenu(run.Logger, pkgbuildDirsByBase, bases, installed, + gotext.Get("PKGBUILDs to edit?"), settings.NoConfirm, run.Cfg.AnswerEdit, nil) if errMenu != nil || len(toEdit) == 0 { return errMenu } // TOFIX: remove or use srcinfo data - if errEdit := editPkgbuilds(cfg.Runtime.Logger, pkgbuildDirsByBase, - toEdit, cfg.Editor, cfg.EditorFlags, nil, settings.NoConfirm); errEdit != nil { + if errEdit := editPkgbuilds(run.Logger, pkgbuildDirsByBase, + toEdit, run.Cfg.Editor, run.Cfg.EditorFlags, nil, settings.NoConfirm); errEdit != nil { return errEdit } - cfg.Runtime.Logger.Println() + run.Logger.Println() - if !text.ContinueTask(os.Stdin, gotext.Get("Proceed with install?"), true, false) { + if !run.Logger.ContinueTask(gotext.Get("Proceed with install?"), true, false) { return settings.ErrUserAbort{} } diff --git a/pkg/menus/menu.go b/pkg/menus/menu.go index 064ad6c..e1872b2 100644 --- a/pkg/menus/menu.go +++ b/pkg/menus/menu.go @@ -2,7 +2,6 @@ package menus import ( "fmt" - "io" "os" "github.com/leonelquinteros/gotext" @@ -14,7 +13,9 @@ import ( mapset "github.com/deckarep/golang-set/v2" ) -func pkgbuildNumberMenu(w io.Writer, pkgbuildDirs map[string]string, bases []string, installed mapset.Set[string]) { +func pkgbuildNumberMenu(logger *text.Logger, pkgbuildDirs map[string]string, + bases []string, installed mapset.Set[string], +) { toPrint := "" for n, pkgBase := range bases { @@ -34,20 +35,20 @@ func pkgbuildNumberMenu(w io.Writer, pkgbuildDirs map[string]string, bases []str toPrint += "\n" } - fmt.Fprint(w, toPrint) + logger.Print(toPrint) } -func selectionMenu(w io.Writer, pkgbuildDirs map[string]string, bases []string, installed mapset.Set[string], +func selectionMenu(logger *text.Logger, 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(w, pkgbuildDirs, bases, installed) + pkgbuildNumberMenu(logger, 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")))) + logger.Infoln(message) + logger.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")))) - selectInput, err := text.GetInput(os.Stdin, defaultAnswer, noConfirm) + selectInput, err := logger.GetInput(defaultAnswer, noConfirm) if err != nil { return nil, err } diff --git a/pkg/news/news.go b/pkg/news/news.go index 2342cf2..016557c 100644 --- a/pkg/news/news.go +++ b/pkg/news/news.go @@ -4,11 +4,9 @@ import ( "bytes" "context" "encoding/xml" - "fmt" "html" "io" "net/http" - "os" "strings" "time" @@ -23,13 +21,13 @@ type item struct { Creator string `xml:"dc:creator"` } -func (item *item) print(buildTime time.Time, all, quiet bool) { +func (item *item) printNews(logger *text.Logger, buildTime time.Time, all, quiet bool) { var fd string date, err := time.Parse(time.RFC1123Z, item.PubDate) if err != nil { - fmt.Fprintln(os.Stderr, err) + logger.Errorln(err) } else { fd = text.FormatTime(int(date.Unix())) if !all && !buildTime.IsZero() { @@ -39,11 +37,11 @@ func (item *item) print(buildTime time.Time, all, quiet bool) { } } - fmt.Println(text.Bold(text.Magenta(fd)), text.Bold(strings.TrimSpace(item.Title))) + logger.Println(text.Bold(text.Magenta(fd)), text.Bold(strings.TrimSpace(item.Title))) if !quiet { desc := strings.TrimSpace(parseNews(item.Description)) - fmt.Println(desc) + logger.Println(desc) } } @@ -60,7 +58,9 @@ type rss struct { Channel channel `xml:"channel"` } -func PrintNewsFeed(ctx context.Context, client *http.Client, cutOffDate time.Time, bottomUp, all, quiet bool) error { +func PrintNewsFeed(ctx context.Context, client *http.Client, logger *text.Logger, + cutOffDate time.Time, bottomUp, all, quiet bool, +) error { req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://archlinux.org/feeds/news", http.NoBody) if err != nil { return err @@ -87,11 +87,11 @@ func PrintNewsFeed(ctx context.Context, client *http.Client, cutOffDate time.Tim if bottomUp { for i := len(rssGot.Channel.Items) - 1; i >= 0; i-- { - rssGot.Channel.Items[i].print(cutOffDate, all, quiet) + rssGot.Channel.Items[i].printNews(logger, cutOffDate, all, quiet) } } else { for i := 0; i < len(rssGot.Channel.Items); i++ { - rssGot.Channel.Items[i].print(cutOffDate, all, quiet) + rssGot.Channel.Items[i].printNews(logger, cutOffDate, all, quiet) } } diff --git a/pkg/news/news_test.go b/pkg/news/news_test.go index 779d31b..c7c4f21 100644 --- a/pkg/news/news_test.go +++ b/pkg/news/news_test.go @@ -8,12 +8,15 @@ import ( "io" "net/http" "os" + "strings" "testing" "time" "github.com/bradleyjkemp/cupaloy" "github.com/stretchr/testify/assert" "gopkg.in/h2non/gock.v1" + + "github.com/Jguer/yay/v12/pkg/text" ) const lastNews = ` @@ -135,17 +138,16 @@ func TestPrintNewsFeed(t *testing.T) { defer gock.Off() - rescueStdout := os.Stdout r, w, _ := os.Pipe() - os.Stdout = w + logger := text.NewLogger(w, w, strings.NewReader(""), false, "logger") - err := PrintNewsFeed(context.Background(), &http.Client{}, tt.args.cutOffDate, tt.args.bottomUp, tt.args.all, tt.args.quiet) + err := PrintNewsFeed(context.Background(), &http.Client{}, logger, + tt.args.cutOffDate, tt.args.bottomUp, tt.args.all, tt.args.quiet) assert.NoError(t, err) w.Close() out, _ := io.ReadAll(r) cupaloy.SnapshotT(t, out) - os.Stdout = rescueStdout }) } } @@ -164,15 +166,14 @@ func TestPrintNewsFeedSameDay(t *testing.T) { defer gock.Off() - rescueStdout := os.Stdout r, w, _ := os.Pipe() - os.Stdout = w + logger := text.NewLogger(w, w, strings.NewReader(""), false, "logger") - err := PrintNewsFeed(context.Background(), &http.Client{}, lastNewsTime, true, false, false) + err := PrintNewsFeed(context.Background(), &http.Client{}, logger, + lastNewsTime, true, false, false) assert.NoError(t, err) w.Close() out, _ := io.ReadAll(r) cupaloy.SnapshotT(t, out) - os.Stdout = rescueStdout } diff --git a/pkg/query/aur_warnings.go b/pkg/query/aur_warnings.go index da75580..a4e1380 100644 --- a/pkg/query/aur_warnings.go +++ b/pkg/query/aur_warnings.go @@ -22,9 +22,6 @@ type AURWarnings struct { } func NewWarnings(logger *text.Logger) *AURWarnings { - if logger == nil { - logger = text.GlobalLogger - } return &AURWarnings{log: logger} } diff --git a/pkg/query/filter.go b/pkg/query/filter.go index 2fa2b14..0e07d97 100644 --- a/pkg/query/filter.go +++ b/pkg/query/filter.go @@ -7,19 +7,19 @@ import ( "github.com/Jguer/yay/v12/pkg/text" ) -func RemoveInvalidTargets(targets []string, mode parser.TargetMode) []string { +func RemoveInvalidTargets(logger *text.Logger, targets []string, mode parser.TargetMode) []string { filteredTargets := make([]string, 0) for _, target := range targets { dbName, _ := text.SplitDBFromName(target) if dbName == "aur" && !mode.AtLeastAUR() { - text.Warnln(gotext.Get("%s: can't use target with option --repo -- skipping", text.Cyan(target))) + logger.Warnln(gotext.Get("%s: can't use target with option --repo -- skipping", text.Cyan(target))) continue } if dbName != "aur" && dbName != "" && !mode.AtLeastRepo() { - text.Warnln(gotext.Get("%s: can't use target with option --aur -- skipping", text.Cyan(target))) + logger.Warnln(gotext.Get("%s: can't use target with option --aur -- skipping", text.Cyan(target))) continue } diff --git a/pkg/query/query_builder.go b/pkg/query/query_builder.go index d016853..e607f9e 100644 --- a/pkg/query/query_builder.go +++ b/pkg/query/query_builder.go @@ -130,7 +130,7 @@ func (a *abstractResults) Less(i, j int) bool { func (s *SourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor, pkgS []string) { var aurErr error - pkgS = RemoveInvalidTargets(pkgS, s.targetMode) + pkgS = RemoveInvalidTargets(s.logger, pkgS, s.targetMode) metric := &metrics.Hamming{ CaseSensitive: false, diff --git a/pkg/settings/pacman.go b/pkg/runtime/pacman.go similarity index 95% rename from pkg/settings/pacman.go rename to pkg/runtime/pacman.go index 323ade0..c63762b 100644 --- a/pkg/settings/pacman.go +++ b/pkg/runtime/pacman.go @@ -1,4 +1,4 @@ -package settings +package runtime import ( "fmt" @@ -10,7 +10,7 @@ import ( "golang.org/x/term" ) -func RetrievePacmanConfig(cmdArgs *parser.Arguments, pacmanConfigPath string) (*pacmanconf.Config, bool, error) { +func retrievePacmanConfig(cmdArgs *parser.Arguments, pacmanConfigPath string) (*pacmanconf.Config, bool, error) { root := "/" if value, _, exists := cmdArgs.GetArg("root", "r"); exists { root = value diff --git a/pkg/settings/pacman_test.go b/pkg/runtime/pacman_test.go similarity index 95% rename from pkg/settings/pacman_test.go rename to pkg/runtime/pacman_test.go index 527d513..8b1890c 100644 --- a/pkg/settings/pacman_test.go +++ b/pkg/runtime/pacman_test.go @@ -1,7 +1,7 @@ //go:build !integration // +build !integration -package settings +package runtime import ( "testing" @@ -46,7 +46,7 @@ func TestPacmanConf(t *testing.T) { }, } - pacmanConf, color, err := RetrievePacmanConfig(parser.MakeArguments(), "../../testdata/pacman.conf") + pacmanConf, color, err := retrievePacmanConfig(parser.MakeArguments(), "../../testdata/pacman.conf") assert.Nil(t, err) assert.NotNil(t, pacmanConf) assert.Equal(t, color, false) diff --git a/pkg/settings/runtime.go b/pkg/runtime/runtime.go similarity index 73% rename from pkg/settings/runtime.go rename to pkg/runtime/runtime.go index 4ee507b..499ee4f 100644 --- a/pkg/settings/runtime.go +++ b/pkg/runtime/runtime.go @@ -1,4 +1,4 @@ -package settings +package runtime import ( "context" @@ -9,8 +9,8 @@ import ( "github.com/leonelquinteros/gotext" - "github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/query" + "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/text" @@ -24,6 +24,7 @@ import ( ) type Runtime struct { + Cfg *settings.Configuration QueryBuilder query.Builder PacmanConf *pacmanconf.Config VCSStore vcs.Store @@ -31,13 +32,12 @@ type Runtime struct { HTTPClient *http.Client VoteClient *vote.Client AURClient aur.QueryClient - DBExecutor db.Executor Logger *text.Logger } -func BuildRuntime(cfg *Configuration, cmdArgs *parser.Arguments, version string) (*Runtime, error) { +func NewRuntime(cfg *settings.Configuration, cmdArgs *parser.Arguments, version string) (*Runtime, error) { logger := text.NewLogger(os.Stdout, os.Stderr, os.Stdin, cfg.Debug, "runtime") - cmdBuilder := cfg.CmdBuilder(nil) + runner := exe.NewOSRunner(logger.Child("runner")) httpClient := &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { @@ -86,6 +86,16 @@ func BuildRuntime(cfg *Configuration, cmdArgs *parser.Arguments, version string) aurCache = aurClient } + pacmanConf, useColor, err := retrievePacmanConfig(cmdArgs, cfg.PacmanConf) + if err != nil { + return nil, err + } + + // FIXME: get rid of global + text.UseColor = useColor + + cmdBuilder := exe.NewCmdBuilder(cfg, runner, logger.Child("cmdbuilder"), pacmanConf.DBPath) + vcsStore := vcs.NewInfoStore( cfg.VCSFilePath, cmdBuilder, logger.Child("vcs")) @@ -94,17 +104,23 @@ func BuildRuntime(cfg *Configuration, cmdArgs *parser.Arguments, version string) return nil, err } - runtime := &Runtime{ - QueryBuilder: nil, - PacmanConf: nil, + queryBuilder := query.NewSourceQueryBuilder( + aurClient, + logger.Child("mixed.querybuilder"), cfg.SortBy, + cfg.Mode, cfg.SearchBy, + cfg.BottomUp, cfg.SingleLineResults, cfg.SeparateSources) + + run := &Runtime{ + Cfg: cfg, + QueryBuilder: queryBuilder, + PacmanConf: pacmanConf, VCSStore: vcsStore, CmdBuilder: cmdBuilder, HTTPClient: &http.Client{}, VoteClient: voteClient, AURClient: aurCache, - DBExecutor: nil, - Logger: text.NewLogger(os.Stdout, os.Stderr, os.Stdin, cfg.Debug, "runtime"), + Logger: logger, } - return runtime, nil + return run, nil } diff --git a/pkg/settings/runtime_test.go b/pkg/runtime/runtime_test.go similarity index 51% rename from pkg/settings/runtime_test.go rename to pkg/runtime/runtime_test.go index f8b4398..e83ce16 100644 --- a/pkg/settings/runtime_test.go +++ b/pkg/runtime/runtime_test.go @@ -1,16 +1,17 @@ //go:build !integration // +build !integration -package settings_test +package runtime_test import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/parser" - "github.com/Jguer/yay/v12/pkg/text" ) func TestBuildRuntime(t *testing.T) { @@ -23,24 +24,23 @@ func TestBuildRuntime(t *testing.T) { AURRPCURL: "https://aur.archlinux.org/rpc", BuildDir: "/tmp", VCSFilePath: "", - Runtime: &settings.Runtime{Logger: text.NewLogger(nil, nil, nil, false, "")}, + PacmanConf: "../../testdata/pacman.conf", } cmdArgs := parser.MakeArguments() version := "1.0.0" // Call the function being tested - runtime, err := settings.BuildRuntime(cfg, cmdArgs, version) + run, err := runtime.NewRuntime(cfg, cmdArgs, version) + require.NoError(t, err) // Assert the function's output - assert.NotNil(t, runtime) - assert.Nil(t, err) - assert.Nil(t, runtime.QueryBuilder) - assert.Nil(t, runtime.PacmanConf) - assert.NotNil(t, runtime.VCSStore) - assert.NotNil(t, runtime.CmdBuilder) - assert.NotNil(t, runtime.HTTPClient) - assert.NotNil(t, runtime.VoteClient) - assert.NotNil(t, runtime.AURClient) - assert.Nil(t, runtime.DBExecutor) - assert.NotNil(t, runtime.Logger) + assert.NotNil(t, run) + assert.NotNil(t, run.QueryBuilder) + assert.NotNil(t, run.PacmanConf) + assert.NotNil(t, run.VCSStore) + assert.NotNil(t, run.CmdBuilder) + assert.NotNil(t, run.HTTPClient) + assert.NotNil(t, run.VoteClient) + assert.NotNil(t, run.AURClient) + assert.NotNil(t, run.Logger) } diff --git a/pkg/settings/args.go b/pkg/settings/args.go index 5617561..92fcac5 100644 --- a/pkg/settings/args.go +++ b/pkg/settings/args.go @@ -5,7 +5,6 @@ import ( "strings" "github.com/Jguer/yay/v12/pkg/settings/parser" - "github.com/Jguer/yay/v12/pkg/text" ) func (c *Configuration) ParseCommandLine(a *parser.Arguments) error { @@ -15,9 +14,6 @@ func (c *Configuration) ParseCommandLine(a *parser.Arguments) error { c.extractYayOptions(a) - // Reload CmdBuilder - c.Runtime.CmdBuilder = c.CmdBuilder(nil) - return nil } @@ -59,7 +55,6 @@ func (c *Configuration) handleOption(option, value string) bool { c.CleanAfter = false case "debug": c.Debug = true - text.GlobalLogger.Debug = true return false case "devel": c.Devel = true diff --git a/pkg/settings/config.go b/pkg/settings/config.go index 350a9a2..23a1e7e 100644 --- a/pkg/settings/config.go +++ b/pkg/settings/config.go @@ -9,7 +9,6 @@ import ( "path/filepath" "strings" - "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/text" @@ -24,53 +23,52 @@ var NoConfirm = false // Configuration stores yay's config. type Configuration struct { - Runtime *Runtime `json:"-"` - AURURL string `json:"aururl"` - AURRPCURL string `json:"aurrpcurl"` - BuildDir string `json:"buildDir"` - Editor string `json:"editor"` - EditorFlags string `json:"editorflags"` - MakepkgBin string `json:"makepkgbin"` - MakepkgConf string `json:"makepkgconf"` - PacmanBin string `json:"pacmanbin"` - PacmanConf string `json:"pacmanconf"` - ReDownload string `json:"redownload"` - AnswerClean string `json:"answerclean"` - AnswerDiff string `json:"answerdiff"` - AnswerEdit string `json:"answeredit"` - AnswerUpgrade string `json:"answerupgrade"` - GitBin string `json:"gitbin"` - GpgBin string `json:"gpgbin"` - GpgFlags string `json:"gpgflags"` - MFlags string `json:"mflags"` - SortBy string `json:"sortby"` - SearchBy string `json:"searchby"` - GitFlags string `json:"gitflags"` - RemoveMake string `json:"removemake"` - SudoBin string `json:"sudobin"` - SudoFlags string `json:"sudoflags"` - Version string `json:"version"` - RequestSplitN int `json:"requestsplitn"` - CompletionInterval int `json:"completionrefreshtime"` - MaxConcurrentDownloads int `json:"maxconcurrentdownloads"` - BottomUp bool `json:"bottomup"` - SudoLoop bool `json:"sudoloop"` - TimeUpdate bool `json:"timeupdate"` - Devel bool `json:"devel"` - CleanAfter bool `json:"cleanAfter"` - Provides bool `json:"provides"` - PGPFetch bool `json:"pgpfetch"` - CleanMenu bool `json:"cleanmenu"` - DiffMenu bool `json:"diffmenu"` - EditMenu bool `json:"editmenu"` - CombinedUpgrade bool `json:"combinedupgrade"` - UseAsk bool `json:"useask"` - BatchInstall bool `json:"batchinstall"` - SingleLineResults bool `json:"singlelineresults"` - SeparateSources bool `json:"separatesources"` - Debug bool `json:"debug"` - UseRPC bool `json:"rpc"` - DoubleConfirm bool `json:"doubleconfirm"` // confirm install before and after build + AURURL string `json:"aururl"` + AURRPCURL string `json:"aurrpcurl"` + BuildDir string `json:"buildDir"` + Editor string `json:"editor"` + EditorFlags string `json:"editorflags"` + MakepkgBin string `json:"makepkgbin"` + MakepkgConf string `json:"makepkgconf"` + PacmanBin string `json:"pacmanbin"` + PacmanConf string `json:"pacmanconf"` + ReDownload string `json:"redownload"` + AnswerClean string `json:"answerclean"` + AnswerDiff string `json:"answerdiff"` + AnswerEdit string `json:"answeredit"` + AnswerUpgrade string `json:"answerupgrade"` + GitBin string `json:"gitbin"` + GpgBin string `json:"gpgbin"` + GpgFlags string `json:"gpgflags"` + MFlags string `json:"mflags"` + SortBy string `json:"sortby"` + SearchBy string `json:"searchby"` + GitFlags string `json:"gitflags"` + RemoveMake string `json:"removemake"` + SudoBin string `json:"sudobin"` + SudoFlags string `json:"sudoflags"` + Version string `json:"version"` + RequestSplitN int `json:"requestsplitn"` + CompletionInterval int `json:"completionrefreshtime"` + MaxConcurrentDownloads int `json:"maxconcurrentdownloads"` + BottomUp bool `json:"bottomup"` + SudoLoop bool `json:"sudoloop"` + TimeUpdate bool `json:"timeupdate"` + Devel bool `json:"devel"` + CleanAfter bool `json:"cleanAfter"` + Provides bool `json:"provides"` + PGPFetch bool `json:"pgpfetch"` + CleanMenu bool `json:"cleanmenu"` + DiffMenu bool `json:"diffmenu"` + EditMenu bool `json:"editmenu"` + CombinedUpgrade bool `json:"combinedupgrade"` + UseAsk bool `json:"useask"` + BatchInstall bool `json:"batchinstall"` + SingleLineResults bool `json:"singlelineresults"` + SeparateSources bool `json:"separatesources"` + Debug bool `json:"debug"` + UseRPC bool `json:"rpc"` + DoubleConfirm bool `json:"doubleconfirm"` // confirm install before and after build CompletionPath string `json:"-"` VCSFilePath string `json:"-"` @@ -237,19 +235,16 @@ func DefaultConfig(version string) *Configuration { Debug: false, UseRPC: true, DoubleConfirm: true, - Runtime: &Runtime{ - Logger: text.GlobalLogger, - }, - Mode: parser.ModeAny, + Mode: parser.ModeAny, } } -func NewConfig(configPath, version string) (*Configuration, error) { +func NewConfig(logger *text.Logger, configPath, version string) (*Configuration, error) { newConfig := DefaultConfig(version) cacheHome, errCache := getCacheHome() - if errCache != nil { - text.Errorln(errCache) + if errCache != nil && logger != nil { + logger.Errorln(errCache) } newConfig.BuildDir = cacheHome @@ -295,27 +290,3 @@ func (c *Configuration) load(configPath string) { } } } - -func (c *Configuration) CmdBuilder(runner exe.Runner) exe.ICmdBuilder { - if runner == nil { - runner = &exe.OSRunner{Log: c.Runtime.Logger.Child("runner")} - } - - return &exe.CmdBuilder{ - GitBin: c.GitBin, - GitFlags: strings.Fields(c.GitFlags), - GPGBin: c.GpgBin, - GPGFlags: strings.Fields(c.GpgFlags), - MakepkgFlags: strings.Fields(c.MFlags), - MakepkgConfPath: c.MakepkgConf, - MakepkgBin: c.MakepkgBin, - SudoBin: c.SudoBin, - SudoFlags: strings.Fields(c.SudoFlags), - SudoLoopEnabled: c.SudoLoop, - PacmanBin: c.PacmanBin, - PacmanConfigPath: c.PacmanConf, - PacmanDBPath: "", - Runner: runner, - Log: c.Runtime.Logger.Child("cmd_builder"), - } -} diff --git a/pkg/settings/config_test.go b/pkg/settings/config_test.go index 8a228f5..b831f17 100644 --- a/pkg/settings/config_test.go +++ b/pkg/settings/config_test.go @@ -36,7 +36,7 @@ func TestNewConfig(t *testing.T) { _, err = f.WriteString(string(configJSON)) assert.NoError(t, err) - newConfig, err := NewConfig(GetConfigPath(), "v1.0.0") + newConfig, err := NewConfig(nil, GetConfigPath(), "v1.0.0") assert.NoError(t, err) assert.Equal(t, filepath.Join(cacheDir, "test-build-dir"), newConfig.BuildDir) @@ -69,7 +69,7 @@ func TestNewConfigAURDEST(t *testing.T) { _, err = f.WriteString(string(configJSON)) assert.NoError(t, err) - newConfig, err := NewConfig(GetConfigPath(), "v1.0.0") + newConfig, err := NewConfig(nil, GetConfigPath(), "v1.0.0") assert.NoError(t, err) assert.Equal(t, filepath.Join(cacheDir, "test-build-dir"), newConfig.BuildDir) @@ -102,7 +102,7 @@ func TestNewConfigAURDESTTildeExpansion(t *testing.T) { _, err = f.WriteString(string(configJSON)) assert.NoError(t, err) - newConfig, err := NewConfig(GetConfigPath(), "v1.0.0") + newConfig, err := NewConfig(nil, GetConfigPath(), "v1.0.0") assert.NoError(t, err) assert.Equal(t, filepath.Join(homeDir, "test-build-dir"), newConfig.BuildDir) diff --git a/pkg/settings/exe/cmd_builder.go b/pkg/settings/exe/cmd_builder.go index 2cffe86..624b7b0 100644 --- a/pkg/settings/exe/cmd_builder.go +++ b/pkg/settings/exe/cmd_builder.go @@ -15,6 +15,7 @@ import ( mapset "github.com/deckarep/golang-set/v2" "github.com/leonelquinteros/gotext" + "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/text" ) @@ -38,7 +39,6 @@ type ICmdBuilder interface { BuildMakepkgCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd BuildPacmanCmd(ctx context.Context, args *parser.Arguments, mode parser.TargetMode, noConfirm bool) *exec.Cmd AddMakepkgFlag(string) - SetPacmanDBPath(string) SudoLoop() } @@ -60,6 +60,26 @@ type CmdBuilder struct { Log *text.Logger } +func NewCmdBuilder(cfg *settings.Configuration, runner Runner, logger *text.Logger, dbPath string) *CmdBuilder { + return &CmdBuilder{ + GitBin: cfg.GitBin, + GitFlags: strings.Fields(cfg.GitFlags), + GPGBin: cfg.GpgBin, + GPGFlags: strings.Fields(cfg.GpgFlags), + MakepkgFlags: strings.Fields(cfg.MFlags), + MakepkgConfPath: cfg.MakepkgConf, + MakepkgBin: cfg.MakepkgBin, + SudoBin: cfg.SudoBin, + SudoFlags: strings.Fields(cfg.SudoFlags), + SudoLoopEnabled: cfg.SudoLoop, + PacmanBin: cfg.PacmanBin, + PacmanConfigPath: cfg.PacmanConf, + PacmanDBPath: dbPath, + Runner: runner, + Log: logger, + } +} + func (c *CmdBuilder) BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd { args := make([]string, len(c.GPGFlags), len(c.GPGFlags)+len(extraArgs)) copy(args, c.GPGFlags) @@ -135,10 +155,6 @@ func (c *CmdBuilder) BuildMakepkgCmd(ctx context.Context, dir string, extraArgs return cmd } -func (c *CmdBuilder) SetPacmanDBPath(dbPath string) { - c.PacmanDBPath = dbPath -} - // deElevateCommand, `systemd-run` code based on pikaur. func (c *CmdBuilder) deElevateCommand(ctx context.Context, cmd *exec.Cmd) *exec.Cmd { if os.Geteuid() != 0 { @@ -242,7 +258,7 @@ func (c *CmdBuilder) waitLock(dbPath string) { time.Sleep(3 * time.Second) if _, err := os.Stat(lockDBPath); err != nil { - fmt.Println() + c.Log.Println() return } diff --git a/pkg/settings/exe/exec.go b/pkg/settings/exe/exec.go index 755af4a..cbc73d4 100644 --- a/pkg/settings/exe/exec.go +++ b/pkg/settings/exe/exec.go @@ -19,6 +19,10 @@ type OSRunner struct { Log *text.Logger } +func NewOSRunner(log *text.Logger) *OSRunner { + return &OSRunner{log} +} + func (r *OSRunner) Show(cmd *exec.Cmd) error { cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr cmd.SysProcAttr = &syscall.SysProcAttr{ diff --git a/pkg/settings/exe/mock.go b/pkg/settings/exe/mock.go index 47c93cb..cbf3238 100644 --- a/pkg/settings/exe/mock.go +++ b/pkg/settings/exe/mock.go @@ -36,6 +36,10 @@ type MockRunner struct { CaptureFn func(cmd *exec.Cmd) (stdout string, stderr string, err error) } +func (m *MockBuilder) BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd { + return exec.CommandContext(ctx, "gpg", extraArgs...) +} + func (m *MockBuilder) BuildMakepkgCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd { var res *exec.Cmd if m.BuildMakepkgCmdFn != nil { diff --git a/pkg/settings/migrations.go b/pkg/settings/migrations.go index 62dad5f..9983fe0 100644 --- a/pkg/settings/migrations.go +++ b/pkg/settings/migrations.go @@ -45,7 +45,7 @@ func DefaultMigrations() []configMigration { } } -func (c *Configuration) RunMigrations(migrations []configMigration, +func (c *Configuration) RunMigrations(logger *text.Logger, migrations []configMigration, configPath, newVersion string, ) error { saveConfig := false @@ -53,7 +53,7 @@ func (c *Configuration) RunMigrations(migrations []configMigration, for _, migration := range migrations { if db.VerCmp(migration.TargetVersion(), c.Version) > 0 { if migration.Do(c) { - text.Infoln("Config migration executed (", + logger.Infoln("Config migration executed (", migration.TargetVersion(), "):", migration) saveConfig = true diff --git a/pkg/settings/migrations_test.go b/pkg/settings/migrations_test.go index b8f203e..a051217 100644 --- a/pkg/settings/migrations_test.go +++ b/pkg/settings/migrations_test.go @@ -16,6 +16,10 @@ import ( "github.com/Jguer/yay/v12/pkg/text" ) +func newTestLogger() *text.Logger { + return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test") +} + func TestMigrationNothingToDo(t *testing.T) { t.Parallel() // Create temporary file for config @@ -28,13 +32,10 @@ func TestMigrationNothingToDo(t *testing.T) { config := Configuration{ Version: "99.0.0", // Create runtime with runtimeVersion - Runtime: &Runtime{ - Logger: text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test"), - }, } // Run Migration - err = config.RunMigrations(DefaultMigrations(), testFilePath, "20.0.0") + err = config.RunMigrations(newTestLogger(), DefaultMigrations(), testFilePath, "20.0.0") require.NoError(t, err) // Check file contents if wantSave otherwise check file empty @@ -53,9 +54,6 @@ func TestProvidesMigrationDo(t *testing.T) { migration := &configProviderMigration{} config := Configuration{ Provides: true, - Runtime: &Runtime{ - Logger: text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test"), - }, } assert.True(t, migration.Do(&config)) @@ -135,13 +133,10 @@ func TestProvidesMigration(t *testing.T) { Version: tc.testConfig.Version, Provides: tc.testConfig.Provides, // Create runtime with runtimeVersion - Runtime: &Runtime{ - Logger: text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test"), - }, } // Run Migration - err = tcConfig.RunMigrations( + err = tcConfig.RunMigrations(newTestLogger(), []configMigration{&configProviderMigration{}}, testFilePath, tc.newVersion) diff --git a/pkg/sync/build/errors.go b/pkg/sync/build/errors.go new file mode 100644 index 0000000..bf35081 --- /dev/null +++ b/pkg/sync/build/errors.go @@ -0,0 +1,62 @@ +package build + +import ( + "errors" + + "github.com/leonelquinteros/gotext" +) + +var ErrInstallRepoPkgs = errors.New(gotext.Get("error installing repo packages")) + +type FailedIgnoredPkgError struct { + pkgErrors map[string]error +} + +func (e *FailedIgnoredPkgError) Error() string { + msg := gotext.Get("Failed to install the following packages. Manual intervention is required:") + + for pkg, err := range e.pkgErrors { + msg += "\n" + pkg + " - " + err.Error() + } + + return msg +} + +type PkgDestNotInListError struct { + name string +} + +func (e *PkgDestNotInListError) Error() string { + return gotext.Get("could not find PKGDEST for: %s", e.name) +} + +type FindPkgDestError struct { + name, pkgDest string +} + +func (e *FindPkgDestError) Error() string { + return gotext.Get( + "the PKGDEST for %s is listed by makepkg but does not exist: %s", + e.name, e.pkgDest) +} + +type SetPkgReasonError struct { + exp bool // explicit +} + +func (e *SetPkgReasonError) Error() string { + reason := gotext.Get("explicit") + if !e.exp { + reason = gotext.Get("dependency") + } + + return gotext.Get("error updating package install reason to %s", reason) +} + +type NoPkgDestsFoundError struct { + dir string +} + +func (e *NoPkgDestsFoundError) Error() string { + return gotext.Get("could not find any package archives listed in %s", e.dir) +} diff --git a/aur_install.go b/pkg/sync/build/installer.go similarity index 93% rename from aur_install.go rename to pkg/sync/build/installer.go index fffd9b3..66988cd 100644 --- a/aur_install.go +++ b/pkg/sync/build/installer.go @@ -1,4 +1,4 @@ -package main +package build import ( "context" @@ -54,12 +54,12 @@ func NewInstaller(dbExecutor db.Executor, } } -func (installer *Installer) CompileFailedAndIgnored() error { +func (installer *Installer) CompileFailedAndIgnored() (map[string]error, error) { if len(installer.failedAndIgnored) == 0 { - return nil + return installer.failedAndIgnored, nil } - return &FailedIgnoredPkgError{ + return installer.failedAndIgnored, &FailedIgnoredPkgError{ pkgErrors: installer.failedAndIgnored, } } @@ -234,12 +234,12 @@ func (installer *Installer) installAURPackages(ctx context.Context, } installer.failedAndIgnored[name] = errMake - text.Errorln(gotext.Get("error making: %s", base), "-", errMake) + installer.log.Errorln(gotext.Get("error making: %s", base), "-", errMake) continue } if len(pkgdests) == 0 { - text.Warnln(gotext.Get("nothing to install for %s", text.Cyan(base))) + installer.log.Warnln(gotext.Get("nothing to install for %s", text.Cyan(base))) continue } @@ -298,10 +298,10 @@ func (installer *Installer) buildPkg(ctx context.Context, case needed && installer.pkgsAreAlreadyInstalled(pkgdests, pkgVersion) || installer.downloadOnly: args = []string{"-c", "--nobuild", "--noextract", "--ignorearch"} pkgdests = map[string]string{} - text.Warnln(gotext.Get("%s is up to date -- skipping", text.Cyan(base+"-"+pkgVersion))) + installer.log.Warnln(gotext.Get("%s is up to date -- skipping", text.Cyan(base+"-"+pkgVersion))) case installer.skipAlreadyBuiltPkg(isTarget, pkgdests): args = []string{"-c", "--nobuild", "--noextract", "--ignorearch"} - text.Warnln(gotext.Get("%s already made -- skipping build", text.Cyan(base+"-"+pkgVersion))) + installer.log.Warnln(gotext.Get("%s already made -- skipping build", text.Cyan(base+"-"+pkgVersion))) default: args = []string{"-cf", "--noconfirm", "--noextract", "--noprepare", "--holdver"} if installIncompatible { @@ -333,10 +333,10 @@ func (installer *Installer) pkgsAreAlreadyInstalled(pkgdests map[string]string, return true } -func pkgsAreBuilt(pkgdests map[string]string) bool { +func pkgsAreBuilt(logger *text.Logger, pkgdests map[string]string) bool { for _, pkgdest := range pkgdests { if _, err := os.Stat(pkgdest); err != nil { - text.Debugln("pkgIsBuilt:", pkgdest, "does not exist") + logger.Debugln("pkgIsBuilt:", pkgdest, "does not exist") return false } } @@ -347,14 +347,14 @@ func pkgsAreBuilt(pkgdests map[string]string) bool { func (installer *Installer) skipAlreadyBuiltPkg(isTarget bool, pkgdests map[string]string) bool { switch installer.rebuildMode { case parser.RebuildModeNo: - return pkgsAreBuilt(pkgdests) + return pkgsAreBuilt(installer.log, pkgdests) case parser.RebuildModeYes: - return !isTarget && pkgsAreBuilt(pkgdests) + return !isTarget && pkgsAreBuilt(installer.log, pkgdests) // case parser.RebuildModeTree: // TODO // case parser.RebuildModeAll: // TODO default: // same as RebuildModeNo - return pkgsAreBuilt(pkgdests) + return pkgsAreBuilt(installer.log, pkgdests) } } diff --git a/aur_install_test.go b/pkg/sync/build/installer_test.go similarity index 98% rename from aur_install_test.go rename to pkg/sync/build/installer_test.go index ecfff9b..a16d433 100644 --- a/aur_install_test.go +++ b/pkg/sync/build/installer_test.go @@ -1,4 +1,4 @@ -package main +package build import ( "context" @@ -21,7 +21,7 @@ import ( "github.com/Jguer/yay/v12/pkg/vcs" ) -func NewTestLogger() *text.Logger { +func newTestLogger() *text.Logger { return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test") } @@ -134,7 +134,7 @@ func TestInstaller_InstallNeeded(t *testing.T) { cmdBuilder.Runner = mockRunner installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, - parser.RebuildModeNo, false, NewTestLogger()) + parser.RebuildModeNo, false, newTestLogger()) cmdArgs := parser.MakeArguments() cmdArgs.AddArg("needed") @@ -408,7 +408,7 @@ func TestInstaller_InstallMixedSourcesAndLayers(t *testing.T) { cmdBuilder.Runner = mockRunner - installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, parser.RebuildModeNo, false, NewTestLogger()) + installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, parser.RebuildModeNo, false, newTestLogger()) cmdArgs := parser.MakeArguments() cmdArgs.AddTarget("yay") @@ -462,7 +462,7 @@ func TestInstaller_RunPostHooks(t *testing.T) { cmdBuilder.Runner = mockRunner installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, - parser.RebuildModeNo, false, NewTestLogger()) + parser.RebuildModeNo, false, newTestLogger()) called := false hook := func(ctx context.Context) error { @@ -593,7 +593,7 @@ func TestInstaller_CompileFailed(t *testing.T) { cmdBuilder.Runner = mockRunner installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, - parser.RebuildModeNo, false, NewTestLogger()) + parser.RebuildModeNo, false, newTestLogger()) cmdArgs := parser.MakeArguments() cmdArgs.AddArg("needed") @@ -609,10 +609,11 @@ func TestInstaller_CompileFailed(t *testing.T) { } else { require.NoError(td, errI) } - err := installer.CompileFailedAndIgnored() + failed, err := installer.CompileFailedAndIgnored() if tc.wantErrCompile { require.Error(td, err) assert.ErrorContains(td, err, "yay") + assert.Len(t, failed, len(tc.targets)) } else { require.NoError(td, err) } @@ -752,7 +753,7 @@ func TestInstaller_InstallSplitPackage(t *testing.T) { cmdBuilder.Runner = mockRunner installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, - parser.RebuildModeNo, false, NewTestLogger()) + parser.RebuildModeNo, false, newTestLogger()) cmdArgs := parser.MakeArguments() cmdArgs.AddTarget("jellyfin") @@ -891,7 +892,7 @@ func TestInstaller_InstallDownloadOnly(t *testing.T) { cmdBuilder.Runner = mockRunner installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, - parser.RebuildModeNo, true, NewTestLogger()) + parser.RebuildModeNo, true, newTestLogger()) cmdArgs := parser.MakeArguments() cmdArgs.AddTarget("yay") @@ -995,7 +996,7 @@ func TestInstaller_InstallGroup(t *testing.T) { cmdBuilder.Runner = mockRunner installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, - parser.RebuildModeNo, true, NewTestLogger()) + parser.RebuildModeNo, true, newTestLogger()) cmdArgs := parser.MakeArguments() cmdArgs.AddTarget("kubernetes-tools") @@ -1213,7 +1214,7 @@ func TestInstaller_InstallRebuild(t *testing.T) { cmdBuilder.Runner = mockRunner installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, - tc.rebuildOption, false, NewTestLogger()) + tc.rebuildOption, false, newTestLogger()) cmdArgs := parser.MakeArguments() cmdArgs.AddTarget("yay") @@ -1298,7 +1299,7 @@ func TestInstaller_InstallUpgrade(t *testing.T) { } installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, tc.targetMode, - parser.RebuildModeNo, false, NewTestLogger()) + parser.RebuildModeNo, false, newTestLogger()) cmdArgs := parser.MakeArguments() cmdArgs.AddArg("u", "upgrades") // Make sure both args are removed diff --git a/install.go b/pkg/sync/build/pkg_archive.go similarity index 68% rename from install.go rename to pkg/sync/build/pkg_archive.go index b4709cc..7563946 100644 --- a/install.go +++ b/pkg/sync/build/pkg_archive.go @@ -1,4 +1,4 @@ -package main +package build import ( "context" @@ -16,164 +16,6 @@ import ( "github.com/Jguer/yay/v12/pkg/vcs" ) -func setPkgReason(ctx context.Context, - cmdBuilder exe.ICmdBuilder, - mode parser.TargetMode, - cmdArgs *parser.Arguments, pkgs []string, exp bool, -) error { - if len(pkgs) == 0 { - return nil - } - - cmdArgs = cmdArgs.CopyGlobal() - if exp { - if err := cmdArgs.AddArg("q", "D", "asexplicit"); err != nil { - return err - } - } else { - if err := cmdArgs.AddArg("q", "D", "asdeps"); err != nil { - return err - } - } - - for _, compositePkgName := range pkgs { - pkgSplit := strings.Split(compositePkgName, "/") - - pkgName := pkgSplit[0] - if len(pkgSplit) > 1 { - pkgName = pkgSplit[1] - } - - cmdArgs.AddTarget(pkgName) - } - - if err := cmdBuilder.Show(cmdBuilder.BuildPacmanCmd(ctx, - cmdArgs, mode, settings.NoConfirm)); err != nil { - return &SetPkgReasonError{exp: exp} - } - - return nil -} - -func asdeps(ctx context.Context, - cmdBuilder exe.ICmdBuilder, - mode parser.TargetMode, cmdArgs *parser.Arguments, pkgs []string, -) error { - return setPkgReason(ctx, cmdBuilder, mode, cmdArgs, pkgs, false) -} - -func asexp(ctx context.Context, - cmdBuilder exe.ICmdBuilder, - mode parser.TargetMode, cmdArgs *parser.Arguments, pkgs []string, -) error { - return setPkgReason(ctx, cmdBuilder, mode, cmdArgs, pkgs, true) -} - -func removeMake(ctx context.Context, config *settings.Configuration, - cmdBuilder exe.ICmdBuilder, makeDeps []string, cmdArgs *parser.Arguments, -) error { - removeArguments := cmdArgs.CopyGlobal() - - err := removeArguments.AddArg("R", "s", "u") - if err != nil { - return err - } - - for _, pkg := range makeDeps { - removeArguments.AddTarget(pkg) - } - - oldValue := settings.NoConfirm - settings.NoConfirm = true - err = cmdBuilder.Show(cmdBuilder.BuildPacmanCmd(ctx, - removeArguments, config.Mode, settings.NoConfirm)) - settings.NoConfirm = oldValue - - return err -} - -func earlyRefresh(ctx context.Context, cfg *settings.Configuration, cmdBuilder exe.ICmdBuilder, cmdArgs *parser.Arguments) error { - arguments := cmdArgs.Copy() - if cfg.CombinedUpgrade { - arguments.DelArg("u", "sysupgrade") - } - arguments.DelArg("s", "search") - arguments.DelArg("i", "info") - arguments.DelArg("l", "list") - arguments.ClearTargets() - - return cmdBuilder.Show(cmdBuilder.BuildPacmanCmd(ctx, - arguments, cfg.Mode, settings.NoConfirm)) -} - -func parsePackageList(ctx context.Context, cmdBuilder exe.ICmdBuilder, - dir string, -) (pkgdests map[string]string, pkgVersion string, err error) { - stdout, stderr, err := cmdBuilder.Capture( - cmdBuilder.BuildMakepkgCmd(ctx, dir, "--packagelist")) - if err != nil { - return nil, "", fmt.Errorf("%s %w", stderr, err) - } - - lines := strings.Split(stdout, "\n") - pkgdests = make(map[string]string) - - for _, line := range lines { - if line == "" { - continue - } - - fileName := filepath.Base(line) - split := strings.Split(fileName, "-") - - if len(split) < 4 { - return nil, "", errors.New(gotext.Get("cannot find package name: %v", split)) - } - - // pkgname-pkgver-pkgrel-arch.pkgext - // This assumes 3 dashes after the pkgname, Will cause an error - // if the PKGEXT contains a dash. Please no one do that. - pkgName := strings.Join(split[:len(split)-3], "-") - pkgVersion = strings.Join(split[len(split)-3:len(split)-1], "-") - pkgdests[pkgName] = line - } - - if len(pkgdests) == 0 { - return nil, "", &NoPkgDestsFoundError{dir} - } - - return pkgdests, pkgVersion, nil -} - -func gitMerge(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) error { - _, stderr, err := cmdBuilder.Capture( - cmdBuilder.BuildGitCmd(ctx, - dir, "reset", "--hard", "HEAD")) - if err != nil { - return errors.New(gotext.Get("error resetting %s: %s", dir, stderr)) - } - - _, stderr, err = cmdBuilder.Capture( - cmdBuilder.BuildGitCmd(ctx, - dir, "merge", "--no-edit", "--ff")) - if err != nil { - return errors.New(gotext.Get("error merging %s: %s", dir, stderr)) - } - - return nil -} - -func mergePkgbuilds(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgbuildDirs map[string]string) error { - for _, dir := range pkgbuildDirs { - err := gitMerge(ctx, cmdBuilder, dir) - if err != nil { - return err - } - } - - return nil -} - func installPkgArchive(ctx context.Context, cmdBuilder exe.ICmdBuilder, mode parser.TargetMode, @@ -228,3 +70,95 @@ func setInstallReason(ctx context.Context, return asexp(ctx, cmdBuilder, mode, cmdArgs, exps) } + +func setPkgReason(ctx context.Context, + cmdBuilder exe.ICmdBuilder, + mode parser.TargetMode, + cmdArgs *parser.Arguments, pkgs []string, exp bool, +) error { + if len(pkgs) == 0 { + return nil + } + + cmdArgs = cmdArgs.CopyGlobal() + if exp { + if err := cmdArgs.AddArg("q", "D", "asexplicit"); err != nil { + return err + } + } else { + if err := cmdArgs.AddArg("q", "D", "asdeps"); err != nil { + return err + } + } + + for _, compositePkgName := range pkgs { + pkgSplit := strings.Split(compositePkgName, "/") + + pkgName := pkgSplit[0] + if len(pkgSplit) > 1 { + pkgName = pkgSplit[1] + } + + cmdArgs.AddTarget(pkgName) + } + + if err := cmdBuilder.Show(cmdBuilder.BuildPacmanCmd(ctx, + cmdArgs, mode, settings.NoConfirm)); err != nil { + return &SetPkgReasonError{exp: exp} + } + + return nil +} + +func asdeps(ctx context.Context, + cmdBuilder exe.ICmdBuilder, + mode parser.TargetMode, cmdArgs *parser.Arguments, pkgs []string, +) error { + return setPkgReason(ctx, cmdBuilder, mode, cmdArgs, pkgs, false) +} + +func asexp(ctx context.Context, + cmdBuilder exe.ICmdBuilder, + mode parser.TargetMode, cmdArgs *parser.Arguments, pkgs []string, +) error { + return setPkgReason(ctx, cmdBuilder, mode, cmdArgs, pkgs, true) +} + +func parsePackageList(ctx context.Context, cmdBuilder exe.ICmdBuilder, + dir string, +) (pkgdests map[string]string, pkgVersion string, err error) { + stdout, stderr, err := cmdBuilder.Capture( + cmdBuilder.BuildMakepkgCmd(ctx, dir, "--packagelist")) + if err != nil { + return nil, "", fmt.Errorf("%s %w", stderr, err) + } + + lines := strings.Split(stdout, "\n") + pkgdests = make(map[string]string) + + for _, line := range lines { + if line == "" { + continue + } + + fileName := filepath.Base(line) + split := strings.Split(fileName, "-") + + if len(split) < 4 { + return nil, "", errors.New(gotext.Get("cannot find package name: %v", split)) + } + + // pkgname-pkgver-pkgrel-arch.pkgext + // This assumes 3 dashes after the pkgname, Will cause an error + // if the PKGEXT contains a dash. Please no one do that. + pkgName := strings.Join(split[:len(split)-3], "-") + pkgVersion = strings.Join(split[len(split)-3:len(split)-1], "-") + pkgdests[pkgName] = line + } + + if len(pkgdests) == 0 { + return nil, "", &NoPkgDestsFoundError{dir} + } + + return pkgdests, pkgVersion, nil +} diff --git a/pkg/pgp/keys.go b/pkg/sync/srcinfo/pgp/keys.go similarity index 76% rename from pkg/pgp/keys.go rename to pkg/sync/srcinfo/pgp/keys.go index 1553288..92ad682 100644 --- a/pkg/pgp/keys.go +++ b/pkg/sync/srcinfo/pgp/keys.go @@ -4,8 +4,6 @@ import ( "bytes" "context" "errors" - "fmt" - "os" "os/exec" "strings" @@ -50,7 +48,7 @@ type GPGCmdBuilder interface { // CheckPgpKeys iterates through the keys listed in the PKGBUILDs and if needed, // asks the user whether yay should try to import them. -func CheckPgpKeys(ctx context.Context, pkgbuildDirsByBase map[string]string, srcinfos map[string]*gosrc.Srcinfo, +func CheckPgpKeys(ctx context.Context, logger *text.Logger, pkgbuildDirsByBase map[string]string, srcinfos map[string]*gosrc.Srcinfo, cmdBuilder GPGCmdBuilder, noConfirm bool, ) ([]string, error) { // Let's check the keys individually, and then we can offer to import @@ -80,24 +78,23 @@ func CheckPgpKeys(ctx context.Context, pkgbuildDirsByBase map[string]string, src return []string{}, nil } - str, err := formatKeysToImport(problematic) + str, err := formatKeysToImport(logger, problematic) if err != nil { return nil, err } - fmt.Println() - fmt.Println(str) + logger.Println("\n", str) - if text.ContinueTask(os.Stdin, gotext.Get("Import?"), true, noConfirm) { - return problematic.toSlice(), importKeys(ctx, cmdBuilder, problematic.toSlice()) + if logger.ContinueTask(gotext.Get("Import?"), true, noConfirm) { + return problematic.toSlice(), importKeys(ctx, logger, cmdBuilder, problematic.toSlice()) } return problematic.toSlice(), nil } // importKeys tries to import the list of keys specified in its argument. -func importKeys(ctx context.Context, cmdBuilder GPGCmdBuilder, keys []string) error { - text.OperationInfoln(gotext.Get("Importing keys with gpg...")) +func importKeys(ctx context.Context, logger *text.Logger, cmdBuilder GPGCmdBuilder, keys []string) error { + logger.OperationInfoln(gotext.Get("Importing keys with gpg...")) if err := cmdBuilder.Show(cmdBuilder.BuildGPGCmd(ctx, append([]string{"--recv-keys"}, keys...)...)); err != nil { return errors.New(gotext.Get("problem importing keys")) @@ -108,14 +105,14 @@ func importKeys(ctx context.Context, cmdBuilder GPGCmdBuilder, keys []string) er // formatKeysToImport receives a set of keys and returns a string containing the // question asking the user wants to import the problematic keys. -func formatKeysToImport(keys pgpKeySet) (string, error) { +func formatKeysToImport(logger *text.Logger, keys pgpKeySet) (string, error) { if len(keys) == 0 { return "", errors.New(gotext.Get("no keys to import")) } var buffer bytes.Buffer - buffer.WriteString(text.SprintOperationInfo(gotext.Get("PGP keys need importing:"))) + buffer.WriteString(logger.SprintOperationInfo(gotext.Get("PGP keys need importing:"))) for key, bases := range keys { pkglist := "" @@ -124,7 +121,7 @@ func formatKeysToImport(keys pgpKeySet) (string, error) { } pkglist = strings.TrimRight(pkglist, " ") - buffer.WriteString("\n" + text.SprintWarn(gotext.Get("%s, required by: %s", text.Cyan(key), text.Cyan(pkglist)))) + buffer.WriteString("\n" + logger.SprintWarn(gotext.Get("%s, required by: %s", text.Cyan(key), text.Cyan(pkglist)))) } return buffer.String(), nil diff --git a/pkg/pgp/keys_test.go b/pkg/sync/srcinfo/pgp/keys_test.go similarity index 96% rename from pkg/pgp/keys_test.go rename to pkg/sync/srcinfo/pgp/keys_test.go index 3ac0df7..c1f3e8e 100644 --- a/pkg/pgp/keys_test.go +++ b/pkg/sync/srcinfo/pgp/keys_test.go @@ -6,6 +6,7 @@ package pgp import ( "context" "fmt" + "io" "os" "os/exec" "sort" @@ -17,8 +18,13 @@ import ( "github.com/stretchr/testify/require" "github.com/Jguer/yay/v12/pkg/settings/exe" + "github.com/Jguer/yay/v12/pkg/text" ) +func newTestLogger() *text.Logger { + return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test") +} + func makeSrcinfo(pkgbase string, pgpkeys ...string) *gosrc.Srcinfo { srcinfo := gosrc.Srcinfo{} srcinfo.Pkgbase = pkgbase @@ -228,7 +234,7 @@ func TestCheckPgpKeys(t *testing.T) { GPGFlags: []string{"--homedir /tmp"}, Runner: mockRunner, } - problematic, err := CheckPgpKeys(context.Background(), tt.pkgs, tt.srcinfos, &cmdBuilder, true) + problematic, err := CheckPgpKeys(context.Background(), newTestLogger(), tt.pkgs, tt.srcinfos, &cmdBuilder, true) require.Len(t, mockRunner.ShowCalls, len(tt.wantShow)) require.Len(t, mockRunner.CaptureCalls, len(tt.wantCapture)) diff --git a/pkg/pgp/testdata/11E521D646982372EB577A1F8F0871F202119294 b/pkg/sync/srcinfo/pgp/testdata/11E521D646982372EB577A1F8F0871F202119294 similarity index 100% rename from pkg/pgp/testdata/11E521D646982372EB577A1F8F0871F202119294 rename to pkg/sync/srcinfo/pgp/testdata/11E521D646982372EB577A1F8F0871F202119294 diff --git a/pkg/pgp/testdata/487EACC08557AD082088DABA1EB2638FF56C0C53 b/pkg/sync/srcinfo/pgp/testdata/487EACC08557AD082088DABA1EB2638FF56C0C53 similarity index 100% rename from pkg/pgp/testdata/487EACC08557AD082088DABA1EB2638FF56C0C53 rename to pkg/sync/srcinfo/pgp/testdata/487EACC08557AD082088DABA1EB2638FF56C0C53 diff --git a/pkg/pgp/testdata/647F28654894E3BD457199BE38DBBDC86092693E b/pkg/sync/srcinfo/pgp/testdata/647F28654894E3BD457199BE38DBBDC86092693E similarity index 100% rename from pkg/pgp/testdata/647F28654894E3BD457199BE38DBBDC86092693E rename to pkg/sync/srcinfo/pgp/testdata/647F28654894E3BD457199BE38DBBDC86092693E diff --git a/pkg/pgp/testdata/A314827C4E4250A204CE6E13284FC34C8E4B1A25 b/pkg/sync/srcinfo/pgp/testdata/A314827C4E4250A204CE6E13284FC34C8E4B1A25 similarity index 100% rename from pkg/pgp/testdata/A314827C4E4250A204CE6E13284FC34C8E4B1A25 rename to pkg/sync/srcinfo/pgp/testdata/A314827C4E4250A204CE6E13284FC34C8E4B1A25 diff --git a/pkg/pgp/testdata/ABAF11C65A2970B130ABE3C479BE3E4300411886 b/pkg/sync/srcinfo/pgp/testdata/ABAF11C65A2970B130ABE3C479BE3E4300411886 similarity index 100% rename from pkg/pgp/testdata/ABAF11C65A2970B130ABE3C479BE3E4300411886 rename to pkg/sync/srcinfo/pgp/testdata/ABAF11C65A2970B130ABE3C479BE3E4300411886 diff --git a/pkg/pgp/testdata/B6C8F98282B944E3B0D5C2530FC3042E345AD05D b/pkg/sync/srcinfo/pgp/testdata/B6C8F98282B944E3B0D5C2530FC3042E345AD05D similarity index 100% rename from pkg/pgp/testdata/B6C8F98282B944E3B0D5C2530FC3042E345AD05D rename to pkg/sync/srcinfo/pgp/testdata/B6C8F98282B944E3B0D5C2530FC3042E345AD05D diff --git a/pkg/pgp/testdata/C52048C0C0748FEE227D47A2702353E0F7E48EDB b/pkg/sync/srcinfo/pgp/testdata/C52048C0C0748FEE227D47A2702353E0F7E48EDB similarity index 100% rename from pkg/pgp/testdata/C52048C0C0748FEE227D47A2702353E0F7E48EDB rename to pkg/sync/srcinfo/pgp/testdata/C52048C0C0748FEE227D47A2702353E0F7E48EDB diff --git a/pkg/srcinfo/service.go b/pkg/sync/srcinfo/service.go similarity index 71% rename from pkg/srcinfo/service.go rename to pkg/sync/srcinfo/service.go index 77dd34e..95599fc 100644 --- a/pkg/srcinfo/service.go +++ b/pkg/sync/srcinfo/service.go @@ -11,28 +11,28 @@ import ( "github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/dep" - "github.com/Jguer/yay/v12/pkg/pgp" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" + "github.com/Jguer/yay/v12/pkg/sync/srcinfo/pgp" "github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/vcs" ) -// TODO: add tests type Service struct { dbExecutor db.Executor cfg *settings.Configuration - cmdBuilder exe.ICmdBuilder + cmdBuilder pgp.GPGCmdBuilder vcsStore vcs.Store + log *text.Logger pkgBuildDirs map[string]string srcInfos map[string]*gosrc.Srcinfo } -func NewService(dbExecutor db.Executor, cfg *settings.Configuration, +func NewService(dbExecutor db.Executor, cfg *settings.Configuration, logger *text.Logger, cmdBuilder exe.ICmdBuilder, vcsStore vcs.Store, pkgBuildDirs map[string]string, ) (*Service, error) { - srcinfos, err := ParseSrcinfoFilesByBase(pkgBuildDirs, true) + srcinfos, err := ParseSrcinfoFilesByBase(logger, pkgBuildDirs, true) if err != nil { panic(err) } @@ -43,6 +43,7 @@ func NewService(dbExecutor db.Executor, cfg *settings.Configuration, vcsStore: vcsStore, pkgBuildDirs: pkgBuildDirs, srcInfos: srcinfos, + log: logger, }, nil } @@ -68,7 +69,7 @@ nextpkg: } func (s *Service) CheckPGPKeys(ctx context.Context) error { - _, errCPK := pgp.CheckPgpKeys(ctx, s.pkgBuildDirs, s.srcInfos, s.cmdBuilder, settings.NoConfirm) + _, errCPK := pgp.CheckPgpKeys(ctx, s.log.Child("pgp"), s.pkgBuildDirs, s.srcInfos, s.cmdBuilder, settings.NoConfirm) return errCPK } @@ -83,15 +84,15 @@ func (s *Service) UpdateVCSStore(ctx context.Context, targets []map[string]*dep. for i := range srcinfo.Packages { for j := range targets { if _, ok := targets[j][srcinfo.Packages[i].Pkgname]; !ok { - text.Debugln("skipping VCS update for", srcinfo.Packages[i].Pkgname, "not in targets") + s.log.Debugln("skipping VCS update for", srcinfo.Packages[i].Pkgname, "not in targets") continue } if _, ok := ignore[srcinfo.Packages[i].Pkgname]; ok { - text.Debugln("skipping VCS update for", srcinfo.Packages[i].Pkgname, "due to install error") + s.log.Debugln("skipping VCS update for", srcinfo.Packages[i].Pkgname, "due to install error") continue } - text.Debugln("checking VCS entry for", srcinfo.Packages[i].Pkgname, fmt.Sprintf("source: %v", srcinfo.Source)) + s.log.Debugln("checking VCS entry for", srcinfo.Packages[i].Pkgname, fmt.Sprintf("source: %v", srcinfo.Source)) s.vcsStore.Update(ctx, srcinfo.Packages[i].Pkgname, srcinfo.Source) } } @@ -100,17 +101,17 @@ func (s *Service) UpdateVCSStore(ctx context.Context, targets []map[string]*dep. return nil } -func ParseSrcinfoFilesByBase(pkgBuildDirs map[string]string, errIsFatal bool) (map[string]*gosrc.Srcinfo, error) { +func ParseSrcinfoFilesByBase(logger *text.Logger, pkgBuildDirs map[string]string, errIsFatal bool) (map[string]*gosrc.Srcinfo, error) { srcinfos := make(map[string]*gosrc.Srcinfo) k := 0 for base, dir := range pkgBuildDirs { - text.OperationInfoln(gotext.Get("(%d/%d) Parsing SRCINFO: %s", k+1, len(pkgBuildDirs), text.Cyan(base))) + logger.OperationInfoln(gotext.Get("(%d/%d) Parsing SRCINFO: %s", k+1, len(pkgBuildDirs), text.Cyan(base))) pkgbuild, err := gosrc.ParseFile(filepath.Join(dir, ".SRCINFO")) if err != nil { if !errIsFatal { - text.Warnln(gotext.Get("failed to parse %s -- skipping: %s", base, err)) + logger.Warnln(gotext.Get("failed to parse %s -- skipping: %s", base, err)) continue } diff --git a/pkg/sync/srcinfo/service_test.go b/pkg/sync/srcinfo/service_test.go new file mode 100644 index 0000000..2d7c95e --- /dev/null +++ b/pkg/sync/srcinfo/service_test.go @@ -0,0 +1,132 @@ +package srcinfo + +import ( + "context" + "io" + "strings" + "testing" + + gosrc "github.com/Morganamilo/go-srcinfo" + + "github.com/stretchr/testify/assert" + + "github.com/Jguer/yay/v12/pkg/db/mock" + "github.com/Jguer/yay/v12/pkg/dep" + "github.com/Jguer/yay/v12/pkg/settings" + "github.com/Jguer/yay/v12/pkg/settings/exe" + "github.com/Jguer/yay/v12/pkg/text" + "github.com/Jguer/yay/v12/pkg/vcs" +) + +func newTestLogger() *text.Logger { + return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test") +} + +func TestNewService(t *testing.T) { + dbExecutor := &mock.DBExecutor{} + cfg := &settings.Configuration{} + cmdBuilder := &exe.MockBuilder{} + vcsStore := &vcs.Mock{} + pkgBuildDirs := map[string]string{ + "jellyfin": "../../../testdata/jfin", + "cephbin": "../../../testdata/cephbin", + } + + srv, err := NewService(dbExecutor, cfg, newTestLogger(), cmdBuilder, vcsStore, pkgBuildDirs) + assert.NoError(t, err) + assert.NotNil(t, srv) + assert.Equal(t, dbExecutor, srv.dbExecutor) + assert.Equal(t, cfg, srv.cfg) + assert.Equal(t, cmdBuilder, srv.cmdBuilder) + assert.Equal(t, vcsStore, srv.vcsStore) + assert.Equal(t, pkgBuildDirs, srv.pkgBuildDirs) + assert.NotNil(t, srv.srcInfos) +} + +func TestService_IncompatiblePkgs(t *testing.T) { + srv := &Service{ + dbExecutor: &mock.DBExecutor{AlpmArchitecturesFn: func() ([]string, error) { + return []string{"x86_64"}, nil + }}, + srcInfos: map[string]*gosrc.Srcinfo{ + "pkg1": { + Package: gosrc.Package{ + Arch: []string{"x86_64", "any"}, + }, + }, + "pkg2": { + Package: gosrc.Package{ + Arch: []string{"any"}, + }, + }, + "pkg3": { + Package: gosrc.Package{ + Arch: []string{"armv7h"}, + }, + }, + "pkg4": { + Package: gosrc.Package{ + Arch: []string{"i683", "x86_64"}, + }, + }, + }, + } + + incompatible, err := srv.IncompatiblePkgs(context.Background()) + assert.NoError(t, err) + assert.ElementsMatch(t, []string{"pkg3"}, incompatible) +} + +func TestService_CheckPGPKeys(t *testing.T) { + srv := &Service{ + log: newTestLogger(), + pkgBuildDirs: map[string]string{ + "pkg1": "/path/to/pkg1", + "pkg2": "/path/to/pkg2", + }, + srcInfos: map[string]*gosrc.Srcinfo{ + "pkg1": { + Packages: []gosrc.Package{ + {Pkgname: "pkg1"}, + }, + }, + "pkg2": { + Packages: []gosrc.Package{ + {Pkgname: "pkg2"}, + }, + }, + }, + } + + err := srv.CheckPGPKeys(context.Background()) + assert.NoError(t, err) +} + +func TestService_UpdateVCSStore(t *testing.T) { + srv := &Service{ + srcInfos: map[string]*gosrc.Srcinfo{ + "pkg1": { + Packages: []gosrc.Package{ + {Pkgname: "pkg1"}, + }, + }, + "pkg2": { + Packages: []gosrc.Package{ + {Pkgname: "pkg2"}, + }, + }, + }, + vcsStore: &vcs.Mock{}, + } + + targets := []map[string]*dep.InstallInfo{ + { + "pkg1": {}, + "pkg2": {}, + }, + } + ignore := map[string]error{} + + err := srv.UpdateVCSStore(context.Background(), targets, ignore) + assert.NoError(t, err) +} diff --git a/pkg/sync/sync.go b/pkg/sync/sync.go new file mode 100644 index 0000000..d5edcde --- /dev/null +++ b/pkg/sync/sync.go @@ -0,0 +1,138 @@ +package sync + +import ( + "context" + + "github.com/Jguer/yay/v12/pkg/completion" + "github.com/Jguer/yay/v12/pkg/db" + "github.com/Jguer/yay/v12/pkg/dep" + "github.com/Jguer/yay/v12/pkg/multierror" + "github.com/Jguer/yay/v12/pkg/runtime" + "github.com/Jguer/yay/v12/pkg/settings" + "github.com/Jguer/yay/v12/pkg/settings/parser" + "github.com/Jguer/yay/v12/pkg/sync/build" + "github.com/Jguer/yay/v12/pkg/sync/srcinfo" + "github.com/Jguer/yay/v12/pkg/sync/workdir" + "github.com/Jguer/yay/v12/pkg/text" + + "github.com/leonelquinteros/gotext" +) + +type OperationService struct { + ctx context.Context + cfg *settings.Configuration + dbExecutor db.Executor + logger *text.Logger +} + +func NewOperationService(ctx context.Context, + dbExecutor db.Executor, + run *runtime.Runtime, +) *OperationService { + return &OperationService{ + ctx: ctx, + cfg: run.Cfg, + dbExecutor: dbExecutor, + logger: run.Logger.Child("operation"), + } +} + +func (o *OperationService) Run(ctx context.Context, run *runtime.Runtime, + cmdArgs *parser.Arguments, + targets []map[string]*dep.InstallInfo, excluded []string, +) error { + if len(targets) == 0 { + o.logger.Println("", gotext.Get("there is nothing to do")) + return nil + } + preparer := workdir.NewPreparer(o.dbExecutor, run.CmdBuilder, o.cfg, o.logger.Child("workdir")) + installer := build.NewInstaller(o.dbExecutor, run.CmdBuilder, + run.VCSStore, o.cfg.Mode, o.cfg.ReBuild, + cmdArgs.ExistsArg("w", "downloadonly"), run.Logger.Child("installer")) + + pkgBuildDirs, errInstall := preparer.Run(ctx, run, targets) + if errInstall != nil { + return errInstall + } + + if cleanFunc := preparer.ShouldCleanMakeDeps(run, cmdArgs); cleanFunc != nil { + installer.AddPostInstallHook(cleanFunc) + } + + if cleanAURDirsFunc := preparer.ShouldCleanAURDirs(run, pkgBuildDirs); cleanAURDirsFunc != nil { + installer.AddPostInstallHook(cleanAURDirsFunc) + } + + go func() { + errComp := completion.Update(ctx, run.HTTPClient, o.dbExecutor, + o.cfg.AURURL, o.cfg.CompletionPath, o.cfg.CompletionInterval, false) + if errComp != nil { + o.logger.Warnln(errComp) + } + }() + + srcInfo, errInstall := srcinfo.NewService(o.dbExecutor, o.cfg, + o.logger.Child("srcinfo"), run.CmdBuilder, run.VCSStore, pkgBuildDirs) + if errInstall != nil { + return errInstall + } + + incompatible, errInstall := srcInfo.IncompatiblePkgs(ctx) + if errInstall != nil { + return errInstall + } + + if errIncompatible := confirmIncompatible(o.logger, incompatible); errIncompatible != nil { + return errIncompatible + } + + if errPGP := srcInfo.CheckPGPKeys(ctx); errPGP != nil { + return errPGP + } + + if errInstall := installer.Install(ctx, cmdArgs, targets, pkgBuildDirs, + excluded, o.manualConfirmRequired(cmdArgs)); errInstall != nil { + return errInstall + } + + var multiErr multierror.MultiError + + failedAndIgnored, err := installer.CompileFailedAndIgnored() + if err != nil { + multiErr.Add(err) + } + + if !cmdArgs.ExistsArg("w", "downloadonly") { + if err := srcInfo.UpdateVCSStore(ctx, targets, failedAndIgnored); err != nil { + o.logger.Warnln(err) + } + } + + if err := installer.RunPostInstallHooks(ctx); err != nil { + multiErr.Add(err) + } + + return multiErr.Return() +} + +func (o *OperationService) manualConfirmRequired(cmdArgs *parser.Arguments) bool { + return (!cmdArgs.ExistsArg("u", "sysupgrade") && cmdArgs.Op != "Y") || o.cfg.DoubleConfirm +} + +func confirmIncompatible(logger *text.Logger, incompatible []string) error { + if len(incompatible) > 0 { + logger.Warnln(gotext.Get("The following packages are not compatible with your architecture:")) + + for _, pkg := range incompatible { + logger.Print(" " + text.Cyan(pkg)) + } + + logger.Println() + + if !logger.ContinueTask(gotext.Get("Try to build them anyway?"), true, settings.NoConfirm) { + return &settings.ErrUserAbort{} + } + } + + return nil +} diff --git a/aur_source.go b/pkg/sync/workdir/aur_source.go similarity index 99% rename from aur_source.go rename to pkg/sync/workdir/aur_source.go index 2642dbb..b517095 100644 --- a/aur_source.go +++ b/pkg/sync/workdir/aur_source.go @@ -1,4 +1,4 @@ -package main +package workdir import ( "context" diff --git a/aur_source_test.go b/pkg/sync/workdir/aur_source_test.go similarity index 99% rename from aur_source_test.go rename to pkg/sync/workdir/aur_source_test.go index bd49f66..266ddac 100644 --- a/aur_source_test.go +++ b/pkg/sync/workdir/aur_source_test.go @@ -1,7 +1,7 @@ //go:build !integration // +build !integration -package main +package workdir import ( "context" diff --git a/pkg/sync/workdir/clean.go b/pkg/sync/workdir/clean.go new file mode 100644 index 0000000..4fb77e8 --- /dev/null +++ b/pkg/sync/workdir/clean.go @@ -0,0 +1,62 @@ +package workdir + +import ( + "context" + + "github.com/leonelquinteros/gotext" + + "github.com/Jguer/yay/v12/pkg/runtime" + "github.com/Jguer/yay/v12/pkg/settings" + "github.com/Jguer/yay/v12/pkg/settings/exe" + "github.com/Jguer/yay/v12/pkg/settings/parser" + "github.com/Jguer/yay/v12/pkg/text" +) + +func removeMake(ctx context.Context, config *settings.Configuration, + cmdBuilder exe.ICmdBuilder, makeDeps []string, cmdArgs *parser.Arguments, +) error { + removeArguments := cmdArgs.CopyGlobal() + + err := removeArguments.AddArg("R", "s", "u") + if err != nil { + return err + } + + for _, pkg := range makeDeps { + removeArguments.AddTarget(pkg) + } + + oldValue := settings.NoConfirm + settings.NoConfirm = true + err = cmdBuilder.Show(cmdBuilder.BuildPacmanCmd(ctx, + removeArguments, config.Mode, settings.NoConfirm)) + settings.NoConfirm = oldValue + + return err +} + +func cleanAfter(ctx context.Context, run *runtime.Runtime, + cmdBuilder exe.ICmdBuilder, pkgbuildDirs map[string]string, +) { + run.Logger.Println(gotext.Get("removing untracked AUR files from cache...")) + + i := 0 + for _, dir := range pkgbuildDirs { + run.Logger.OperationInfoln(gotext.Get("Cleaning (%d/%d): %s", i+1, len(pkgbuildDirs), text.Cyan(dir))) + + _, stderr, err := cmdBuilder.Capture( + cmdBuilder.BuildGitCmd( + ctx, dir, "reset", "--hard", "HEAD")) + if err != nil { + run.Logger.Errorln(gotext.Get("error resetting %s: %s", dir, stderr)) + } + + if err := run.CmdBuilder.Show( + run.CmdBuilder.BuildGitCmd( + ctx, dir, "clean", "-fx", "--exclude", "*.pkg.*")); err != nil { + run.Logger.Errorln(err) + } + + i++ + } +} diff --git a/pkg/sync/workdir/merge.go b/pkg/sync/workdir/merge.go new file mode 100644 index 0000000..593f218 --- /dev/null +++ b/pkg/sync/workdir/merge.go @@ -0,0 +1,39 @@ +package workdir + +import ( + "context" + "errors" + + "github.com/leonelquinteros/gotext" + + "github.com/Jguer/yay/v12/pkg/settings/exe" +) + +func gitMerge(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) error { + _, stderr, err := cmdBuilder.Capture( + cmdBuilder.BuildGitCmd(ctx, + dir, "reset", "--hard", "HEAD")) + if err != nil { + return errors.New(gotext.Get("error resetting %s: %s", dir, stderr)) + } + + _, stderr, err = cmdBuilder.Capture( + cmdBuilder.BuildGitCmd(ctx, + dir, "merge", "--no-edit", "--ff")) + if err != nil { + return errors.New(gotext.Get("error merging %s: %s", dir, stderr)) + } + + return nil +} + +func mergePkgbuilds(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgbuildDirs map[string]string) error { + for _, dir := range pkgbuildDirs { + err := gitMerge(ctx, cmdBuilder, dir) + if err != nil { + return err + } + } + + return nil +} diff --git a/preparer.go b/pkg/sync/workdir/preparer.go similarity index 74% rename from preparer.go rename to pkg/sync/workdir/preparer.go index dd73260..d3529b5 100644 --- a/preparer.go +++ b/pkg/sync/workdir/preparer.go @@ -1,4 +1,4 @@ -package main +package workdir import ( "context" @@ -12,9 +12,11 @@ import ( "github.com/Jguer/yay/v12/pkg/dep" "github.com/Jguer/yay/v12/pkg/download" "github.com/Jguer/yay/v12/pkg/menus" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/parser" + "github.com/Jguer/yay/v12/pkg/sync/build" "github.com/Jguer/yay/v12/pkg/text" gosrc "github.com/Morganamilo/go-srcinfo" @@ -29,7 +31,7 @@ const ( PreDownloadSourcesHook HookType = "pre-download-sources" ) -type HookFn func(ctx context.Context, config *settings.Configuration, w io.Writer, +type HookFn func(ctx context.Context, run *runtime.Runtime, w io.Writer, pkgbuildDirsByBase map[string]string, installed mapset.Set[string], ) error @@ -45,12 +47,13 @@ type Preparer struct { cfg *settings.Configuration hooks []Hook downloadSources bool + log *text.Logger makeDeps []string } func NewPreparerWithoutHooks(dbExecutor db.Executor, cmdBuilder exe.ICmdBuilder, - cfg *settings.Configuration, downloadSources bool, + cfg *settings.Configuration, logger *text.Logger, downloadSources bool, ) *Preparer { return &Preparer{ dbExecutor: dbExecutor, @@ -58,13 +61,14 @@ func NewPreparerWithoutHooks(dbExecutor db.Executor, cmdBuilder exe.ICmdBuilder, cfg: cfg, hooks: []Hook{}, downloadSources: downloadSources, + log: logger, } } func NewPreparer(dbExecutor db.Executor, cmdBuilder exe.ICmdBuilder, - cfg *settings.Configuration, + cfg *settings.Configuration, logger *text.Logger, ) *Preparer { - preper := NewPreparerWithoutHooks(dbExecutor, cmdBuilder, cfg, true) + preper := NewPreparerWithoutHooks(dbExecutor, cmdBuilder, cfg, logger, true) if cfg.CleanMenu { preper.hooks = append(preper.hooks, Hook{ @@ -93,20 +97,20 @@ func NewPreparer(dbExecutor db.Executor, cmdBuilder exe.ICmdBuilder, return preper } -func (preper *Preparer) ShouldCleanAURDirs(pkgBuildDirs map[string]string) PostInstallHookFunc { +func (preper *Preparer) ShouldCleanAURDirs(run *runtime.Runtime, pkgBuildDirs map[string]string) build.PostInstallHookFunc { if !preper.cfg.CleanAfter || len(pkgBuildDirs) == 0 { return nil } - text.Debugln("added post install hook to clean up AUR dirs", pkgBuildDirs) + preper.log.Debugln("added post install hook to clean up AUR dirs", pkgBuildDirs) return func(ctx context.Context) error { - cleanAfter(ctx, preper.cfg, preper.cfg.Runtime.CmdBuilder, pkgBuildDirs) + cleanAfter(ctx, run, run.CmdBuilder, pkgBuildDirs) return nil } } -func (preper *Preparer) ShouldCleanMakeDeps(cmdArgs *parser.Arguments) PostInstallHookFunc { +func (preper *Preparer) ShouldCleanMakeDeps(run *runtime.Runtime, cmdArgs *parser.Arguments) build.PostInstallHookFunc { if len(preper.makeDeps) == 0 { return nil } @@ -118,25 +122,25 @@ func (preper *Preparer) ShouldCleanMakeDeps(cmdArgs *parser.Arguments) PostInsta return nil default: isYesDefault := preper.cfg.RemoveMake == "askyes" - if !text.ContinueTask(os.Stdin, gotext.Get("Remove make dependencies after install?"), + if !preper.log.ContinueTask(gotext.Get("Remove make dependencies after install?"), isYesDefault, settings.NoConfirm) { return nil } } - text.Debugln("added post install hook to clean up AUR makedeps", preper.makeDeps) + preper.log.Debugln("added post install hook to clean up AUR makedeps", preper.makeDeps) return func(ctx context.Context) error { - return removeMake(ctx, preper.cfg, preper.cfg.Runtime.CmdBuilder, preper.makeDeps, cmdArgs) + return removeMake(ctx, preper.cfg, run.CmdBuilder, preper.makeDeps, cmdArgs) } } -func (preper *Preparer) Run(ctx context.Context, - w io.Writer, targets []map[string]*dep.InstallInfo, +func (preper *Preparer) Run(ctx context.Context, run *runtime.Runtime, + targets []map[string]*dep.InstallInfo, ) (pkgbuildDirsByBase map[string]string, err error) { - preper.Present(w, targets) + preper.Present(targets) - pkgBuildDirs, err := preper.PrepareWorkspace(ctx, targets) + pkgBuildDirs, err := preper.PrepareWorkspace(ctx, run, targets) if err != nil { return nil, err } @@ -144,7 +148,7 @@ func (preper *Preparer) Run(ctx context.Context, return pkgBuildDirs, nil } -func (preper *Preparer) Present(w io.Writer, targets []map[string]*dep.InstallInfo) { +func (preper *Preparer) Present(targets []map[string]*dep.InstallInfo) { pkgsBySourceAndReason := map[string]map[string][]string{} for _, layer := range targets { @@ -173,7 +177,7 @@ func (preper *Preparer) Present(w io.Writer, targets []map[string]*dep.InstallIn for source, pkgsByReason := range pkgsBySourceAndReason { for reason, pkgs := range pkgsByReason { - fmt.Fprintf(w, text.Bold("%s %s (%d):")+" %s\n", + preper.log.Printf(text.Bold("%s %s (%d):")+" %s\n", source, reason, len(pkgs), @@ -182,7 +186,9 @@ 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) { +func (preper *Preparer) PrepareWorkspace(ctx context.Context, + run *runtime.Runtime, targets []map[string]*dep.InstallInfo, +) (map[string]string, error) { aurBasesToClone := mapset.NewThreadUnsafeSet[string]() pkgBuildDirsByBase := make(map[string]string, len(targets)) @@ -203,7 +209,7 @@ func (preper *Preparer) PrepareWorkspace(ctx context.Context, targets []map[stri } if _, errA := download.AURPKGBUILDRepos(ctx, - preper.cmdBuilder, aurBasesToClone.ToSlice(), + preper.cmdBuilder, preper.log.Child("download"), aurBasesToClone.ToSlice(), preper.cfg.AURURL, preper.cfg.BuildDir, false); errA != nil { return nil, errA } @@ -220,7 +226,7 @@ func (preper *Preparer) PrepareWorkspace(ctx context.Context, targets []map[stri remoteNamesCache := mapset.NewThreadUnsafeSet(remoteNames...) for _, hookFn := range preper.hooks { if hookFn.Type == PreDownloadSourcesHook { - if err := hookFn.Hookfn(ctx, preper.cfg, os.Stdout, pkgBuildDirsByBase, remoteNamesCache); err != nil { + if err := hookFn.Hookfn(ctx, run, os.Stdout, pkgBuildDirsByBase, remoteNamesCache); err != nil { return nil, err } } @@ -228,7 +234,7 @@ func (preper *Preparer) PrepareWorkspace(ctx context.Context, targets []map[stri if errP := downloadPKGBUILDSourceFanout(ctx, preper.cmdBuilder, pkgBuildDirsByBase, false, preper.cfg.MaxConcurrentDownloads); errP != nil { - text.Errorln(errP) + preper.log.Errorln(errP) } return pkgBuildDirsByBase, nil @@ -242,7 +248,7 @@ func (preper *Preparer) needToCloneAURBase(installInfo *dep.InstallInfo, pkgbuil srcinfoFile := filepath.Join(pkgbuildDir, ".SRCINFO") if pkgbuild, err := gosrc.ParseFile(srcinfoFile); err == nil { if db.VerCmp(pkgbuild.Version(), installInfo.Version) >= 0 { - text.OperationInfoln( + preper.log.OperationInfoln( gotext.Get("PKGBUILD up to date, skipping download: %s", text.Cyan(*installInfo.AURBase))) return false diff --git a/preparer_test.go b/pkg/sync/workdir/preparer_test.go similarity index 82% rename from preparer_test.go rename to pkg/sync/workdir/preparer_test.go index 2bc5f8a..163477f 100644 --- a/preparer_test.go +++ b/pkg/sync/workdir/preparer_test.go @@ -1,16 +1,23 @@ //go:build !integration // +build !integration -package main +package workdir import ( + "io" + "strings" "testing" "github.com/stretchr/testify/assert" "github.com/Jguer/yay/v12/pkg/settings" + "github.com/Jguer/yay/v12/pkg/text" ) +func newTestLogger() *text.Logger { + return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test") +} + // Test order of pre-download-sources hooks func TestPreDownloadSourcesHooks(t *testing.T) { testCases := []struct { @@ -49,7 +56,7 @@ func TestPreDownloadSourcesHooks(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - preper := NewPreparer(nil, nil, tc.cfg) + preper := NewPreparer(nil, nil, tc.cfg, newTestLogger()) assert.Len(t, preper.hooks, len(tc.wantHook)) diff --git a/pkg/text/input.go b/pkg/text/input.go index 1739420..bf36ec1 100644 --- a/pkg/text/input.go +++ b/pkg/text/input.go @@ -3,14 +3,18 @@ package text import ( "bufio" "fmt" - "io" + "strings" + "unicode" + "unicode/utf8" + + "github.com/leonelquinteros/gotext" ) func (l *Logger) GetInput(defaultValue string, noConfirm bool) (string, error) { - Info() + l.Info() if defaultValue != "" || noConfirm { - fmt.Println(defaultValue) + l.Println(defaultValue) return defaultValue, nil } @@ -28,6 +32,48 @@ func (l *Logger) GetInput(defaultValue string, noConfirm bool) (string, error) { return string(buf), nil } -func GetInput(r io.Reader, defaultValue string, noConfirm bool) (string, error) { - return GlobalLogger.GetInput(defaultValue, noConfirm) +// ContinueTask prompts if user wants to continue task. +// If NoConfirm is set the action will continue without user input. +func (l *Logger) ContinueTask(s string, preset, noConfirm bool) bool { + if noConfirm { + return preset + } + + var ( + response string + postFix string + n string + y string + yes = gotext.Get("yes") + no = gotext.Get("no") + ) + + // Only use localized "y" and "n" if they are latin characters. + if nRune, _ := utf8.DecodeRuneInString(no); unicode.Is(unicode.Latin, nRune) { + n = string(nRune) + } else { + n = nDefault + } + + if yRune, _ := utf8.DecodeRuneInString(yes); unicode.Is(unicode.Latin, yRune) { + y = string(yRune) + } else { + y = yDefault + } + + if preset { // If default behavior is true, use y as default. + postFix = fmt.Sprintf(" [%s/%s] ", strings.ToUpper(y), n) + } else { // If default behavior is anything else, use n as default. + postFix = fmt.Sprintf(" [%s/%s] ", y, strings.ToUpper(n)) + } + + l.OperationInfo(Bold(s), Bold(postFix)) + + if _, err := fmt.Fscanln(l.r, &response); err != nil { + return preset + } + + return strings.EqualFold(response, yes) || + strings.EqualFold(response, y) || + (!strings.EqualFold(yDefault, n) && strings.EqualFold(response, yDefault)) } diff --git a/pkg/text/print.go b/pkg/text/print.go deleted file mode 100644 index ae939bf..0000000 --- a/pkg/text/print.go +++ /dev/null @@ -1,139 +0,0 @@ -package text - -import ( - "fmt" - "os" - "strconv" - "strings" - "syscall" - "unicode" - - "github.com/leonelquinteros/gotext" - "golang.org/x/sys/unix" -) - -const ( - arrow = "==>" - smallArrow = " ->" - opSymbol = "::" -) - -var ( - cachedColumnCount = -1 - GlobalLogger = NewLogger(os.Stdout, os.Stderr, os.Stdin, false, "global") -) - -func Debugln(a ...interface{}) { - GlobalLogger.Debugln(a...) -} - -func OperationInfoln(a ...interface{}) { - GlobalLogger.OperationInfoln(a...) -} - -func OperationInfo(a ...interface{}) { - GlobalLogger.OperationInfo(a...) -} - -func SprintOperationInfo(a ...interface{}) string { - return GlobalLogger.SprintOperationInfo(a...) -} - -func Info(a ...interface{}) { - GlobalLogger.Info(a...) -} - -func Infoln(a ...interface{}) { - GlobalLogger.Infoln(a...) -} - -func SprintWarn(a ...interface{}) string { - return GlobalLogger.SprintWarn(a...) -} - -func Warn(a ...interface{}) { - GlobalLogger.Warn(a...) -} - -func Warnln(a ...interface{}) { - GlobalLogger.Warnln(a...) -} - -func SprintError(a ...interface{}) string { - return GlobalLogger.SprintError(a...) -} - -func Error(a ...interface{}) { - GlobalLogger.Error(a...) -} - -func Errorln(a ...interface{}) { - GlobalLogger.Errorln(a...) -} - -func getColumnCount() int { - if cachedColumnCount > 0 { - return cachedColumnCount - } - - if count, err := strconv.Atoi(os.Getenv("COLUMNS")); err == nil { - cachedColumnCount = count - return cachedColumnCount - } - - if ws, err := unix.IoctlGetWinsize(syscall.Stdout, unix.TIOCGWINSZ); err == nil { - cachedColumnCount = int(ws.Col) - return cachedColumnCount - } - - return 80 -} - -func PrintInfoValue(key string, values ...string) { - const ( - keyLength = 32 - delimCount = 2 - ) - - specialWordsCount := 0 - - for _, runeValue := range key { - // CJK handling: the character 'ー' is Katakana - // but if use unicode.Katakana, it will return false - if unicode.IsOneOf([]*unicode.RangeTable{ - unicode.Han, - unicode.Hiragana, - unicode.Katakana, - unicode.Hangul, - }, runeValue) || runeValue == 'ー' { - specialWordsCount++ - } - } - - keyTextCount := specialWordsCount - keyLength + delimCount - str := fmt.Sprintf(Bold("%-*s: "), keyTextCount, key) - - if len(values) == 0 || (len(values) == 1 && values[0] == "") { - fmt.Fprintf(os.Stdout, "%s%s\n", str, gotext.Get("None")) - return - } - - maxCols := getColumnCount() - cols := keyLength + len(values[0]) - str += values[0] - - for _, value := range values[1:] { - if maxCols > keyLength && cols+len(value)+delimCount >= maxCols { - cols = keyLength - str += "\n" + strings.Repeat(" ", keyLength) - } else if cols != keyLength { - str += strings.Repeat(" ", delimCount) - cols += delimCount - } - - str += value - cols += len(value) - } - - fmt.Println(str) -} diff --git a/pkg/text/service.go b/pkg/text/service.go index 29733b3..71105f8 100644 --- a/pkg/text/service.go +++ b/pkg/text/service.go @@ -5,6 +5,12 @@ import ( "io" ) +const ( + arrow = "==>" + smallArrow = " ->" + opSymbol = "::" +) + type Logger struct { name string Debug bool diff --git a/pkg/text/text.go b/pkg/text/text.go index f10f1e5..24db4fc 100644 --- a/pkg/text/text.go +++ b/pkg/text/text.go @@ -1,13 +1,8 @@ package text import ( - "fmt" - "io" "strings" "unicode" - "unicode/utf8" - - "github.com/leonelquinteros/gotext" ) const ( @@ -52,49 +47,3 @@ func LessRunes(iRunes, jRunes []rune) bool { return len(iRunes) < len(jRunes) } - -// ContinueTask prompts if user wants to continue task. -// If NoConfirm is set the action will continue without user input. -func ContinueTask(input io.Reader, s string, preset, noConfirm bool) bool { - if noConfirm { - return preset - } - - var ( - response string - postFix string - n string - y string - yes = gotext.Get("yes") - no = gotext.Get("no") - ) - - // Only use localized "y" and "n" if they are latin characters. - if nRune, _ := utf8.DecodeRuneInString(no); unicode.Is(unicode.Latin, nRune) { - n = string(nRune) - } else { - n = nDefault - } - - if yRune, _ := utf8.DecodeRuneInString(yes); unicode.Is(unicode.Latin, yRune) { - y = string(yRune) - } else { - y = yDefault - } - - if preset { // If default behavior is true, use y as default. - postFix = fmt.Sprintf(" [%s/%s] ", strings.ToUpper(y), n) - } else { // If default behavior is anything else, use n as default. - postFix = fmt.Sprintf(" [%s/%s] ", y, strings.ToUpper(n)) - } - - OperationInfo(Bold(s), Bold(postFix)) - - if _, err := fmt.Fscanln(input, &response); err != nil { - return preset - } - - return strings.EqualFold(response, yes) || - strings.EqualFold(response, y) || - (!strings.EqualFold(yDefault, n) && strings.EqualFold(response, yDefault)) -} diff --git a/pkg/text/text_test.go b/pkg/text/text_test.go index ddafeac..71a5f0b 100644 --- a/pkg/text/text_test.go +++ b/pkg/text/text_test.go @@ -4,6 +4,7 @@ package text import ( + "io" "os" "path" "strings" @@ -74,7 +75,8 @@ func TestContinueTask(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // create io.Reader with value of input in := strings.NewReader(tt.args.input) - got := ContinueTask(in, tt.args.s, tt.args.preset, tt.args.noConfirm) + logger := NewLogger(io.Discard, io.Discard, in, false, "test") + got := logger.ContinueTask(tt.args.s, tt.args.preset, tt.args.noConfirm) require.Equal(t, tt.want, got) }) } @@ -120,7 +122,8 @@ msgstr "да" for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { in := strings.NewReader(tt.args.input) - got := ContinueTask(in, tt.args.s, tt.args.preset, tt.args.noConfirm) + logger := NewLogger(io.Discard, io.Discard, in, false, "test") + got := logger.ContinueTask(tt.args.s, tt.args.preset, tt.args.noConfirm) require.Equal(t, tt.want, got) }) } @@ -168,7 +171,8 @@ msgstr "ja" for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { in := strings.NewReader(tt.args.input) - got := ContinueTask(in, tt.args.s, tt.args.preset, tt.args.noConfirm) + logger := NewLogger(io.Discard, io.Discard, in, false, "test") + got := logger.ContinueTask(tt.args.s, tt.args.preset, tt.args.noConfirm) require.Equal(t, tt.want, got) }) } diff --git a/pkg/vcs/vcs_test.go b/pkg/vcs/vcs_test.go index 05a2bb9..873e3ad 100644 --- a/pkg/vcs/vcs_test.go +++ b/pkg/vcs/vcs_test.go @@ -7,7 +7,6 @@ import ( "context" "encoding/json" "errors" - "fmt" "io" "os" "os/exec" @@ -24,6 +23,10 @@ import ( "github.com/Jguer/yay/v12/pkg/text" ) +func newTestLogger() *text.Logger { + return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test") +} + func TestParsing(t *testing.T) { t.Parallel() type source struct { @@ -232,7 +235,7 @@ func TestInfoStoreToUpgrade(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() v := &InfoStore{ - logger: text.GlobalLogger, + logger: newTestLogger(), CmdBuilder: tt.fields.CmdBuilder, OriginsByPackage: map[string]OriginInfoByURL{ "yay": tt.args.infos, @@ -365,7 +368,7 @@ func TestInfoStore_NeedsUpdate(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() v := &InfoStore{ - logger: text.GlobalLogger, + logger: newTestLogger(), CmdBuilder: tt.fields.CmdBuilder, } got := v.needsUpdate(context.Background(), tt.args.infos) @@ -415,7 +418,7 @@ func TestInfoStore_Update(t *testing.T) { t.Parallel() v := &InfoStore{ OriginsByPackage: tt.fields.OriginsByPackage, - logger: text.GlobalLogger, + logger: newTestLogger(), FilePath: filePath, CmdBuilder: tt.fields.CmdBuilder, } @@ -429,7 +432,6 @@ func TestInfoStore_Update(t *testing.T) { cupaloy.SnapshotT(t, marshalledinfo) v.Load() - fmt.Println(v.OriginsByPackage) assert.Len(t, tt.fields.OriginsByPackage, 1) marshalledinfo, err = json.MarshalIndent(tt.fields.OriginsByPackage, "", "\t") @@ -479,7 +481,7 @@ func TestInfoStore_Remove(t *testing.T) { t.Parallel() v := &InfoStore{ OriginsByPackage: tt.fields.OriginsByPackage, - logger: text.GlobalLogger, + logger: newTestLogger(), FilePath: filePath, } v.RemovePackages(tt.args.pkgs) diff --git a/print.go b/print.go index d9775f3..243de29 100644 --- a/print.go +++ b/print.go @@ -6,14 +6,19 @@ import ( "io" "os" "strconv" + "strings" + "syscall" + "unicode" aur "github.com/Jguer/aur" mapset "github.com/deckarep/golang-set/v2" "github.com/leonelquinteros/gotext" + "golang.org/x/sys/unix" "github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/dep" "github.com/Jguer/yay/v12/pkg/query" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/text" @@ -21,47 +26,47 @@ import ( ) // printInfo prints package info like pacman -Si. -func printInfo(config *settings.Configuration, a *aur.Pkg, extendedInfo bool) { - text.PrintInfoValue(gotext.Get("Repository"), "aur") - text.PrintInfoValue(gotext.Get("Name"), a.Name) - text.PrintInfoValue(gotext.Get("Version"), a.Version) - text.PrintInfoValue(gotext.Get("Description"), a.Description) - text.PrintInfoValue(gotext.Get("URL"), a.URL) - text.PrintInfoValue(gotext.Get("Licenses"), a.License...) - text.PrintInfoValue(gotext.Get("Groups"), a.Groups...) - text.PrintInfoValue(gotext.Get("Provides"), a.Provides...) - text.PrintInfoValue(gotext.Get("Depends On"), a.Depends...) - text.PrintInfoValue(gotext.Get("Optional Deps"), a.OptDepends...) - text.PrintInfoValue(gotext.Get("Make Deps"), a.MakeDepends...) - text.PrintInfoValue(gotext.Get("Check Deps"), a.CheckDepends...) - text.PrintInfoValue(gotext.Get("Conflicts With"), a.Conflicts...) - text.PrintInfoValue(gotext.Get("Replaces"), a.Replaces...) - text.PrintInfoValue(gotext.Get("AUR URL"), config.AURURL+"/packages/"+a.Name) - text.PrintInfoValue(gotext.Get("First Submitted"), text.FormatTimeQuery(a.FirstSubmitted)) - text.PrintInfoValue(gotext.Get("Keywords"), a.Keywords...) - text.PrintInfoValue(gotext.Get("Last Modified"), text.FormatTimeQuery(a.LastModified)) - text.PrintInfoValue(gotext.Get("Maintainer"), a.Maintainer) - text.PrintInfoValue(gotext.Get("Popularity"), fmt.Sprintf("%f", a.Popularity)) - text.PrintInfoValue(gotext.Get("Votes"), fmt.Sprintf("%d", a.NumVotes)) +func printInfo(logger *text.Logger, config *settings.Configuration, a *aur.Pkg, extendedInfo bool) { + printInfoValue(logger, gotext.Get("Repository"), "aur") + printInfoValue(logger, gotext.Get("Name"), a.Name) + printInfoValue(logger, gotext.Get("Version"), a.Version) + printInfoValue(logger, gotext.Get("Description"), a.Description) + printInfoValue(logger, gotext.Get("URL"), a.URL) + printInfoValue(logger, gotext.Get("Licenses"), a.License...) + printInfoValue(logger, gotext.Get("Groups"), a.Groups...) + printInfoValue(logger, gotext.Get("Provides"), a.Provides...) + printInfoValue(logger, gotext.Get("Depends On"), a.Depends...) + printInfoValue(logger, gotext.Get("Optional Deps"), a.OptDepends...) + printInfoValue(logger, gotext.Get("Make Deps"), a.MakeDepends...) + printInfoValue(logger, gotext.Get("Check Deps"), a.CheckDepends...) + printInfoValue(logger, gotext.Get("Conflicts With"), a.Conflicts...) + printInfoValue(logger, gotext.Get("Replaces"), a.Replaces...) + printInfoValue(logger, gotext.Get("AUR URL"), config.AURURL+"/packages/"+a.Name) + printInfoValue(logger, gotext.Get("First Submitted"), text.FormatTimeQuery(a.FirstSubmitted)) + printInfoValue(logger, gotext.Get("Keywords"), a.Keywords...) + printInfoValue(logger, gotext.Get("Last Modified"), text.FormatTimeQuery(a.LastModified)) + printInfoValue(logger, gotext.Get("Maintainer"), a.Maintainer) + printInfoValue(logger, gotext.Get("Popularity"), fmt.Sprintf("%f", a.Popularity)) + printInfoValue(logger, gotext.Get("Votes"), fmt.Sprintf("%d", a.NumVotes)) if a.OutOfDate != 0 { - text.PrintInfoValue(gotext.Get("Out-of-date"), text.FormatTimeQuery(a.OutOfDate)) + printInfoValue(logger, gotext.Get("Out-of-date"), text.FormatTimeQuery(a.OutOfDate)) } else { - text.PrintInfoValue(gotext.Get("Out-of-date"), "No") + printInfoValue(logger, gotext.Get("Out-of-date"), "No") } if extendedInfo { - text.PrintInfoValue("ID", fmt.Sprintf("%d", a.ID)) - text.PrintInfoValue(gotext.Get("Package Base ID"), fmt.Sprintf("%d", a.PackageBaseID)) - text.PrintInfoValue(gotext.Get("Package Base"), a.PackageBase) - text.PrintInfoValue(gotext.Get("Snapshot URL"), config.AURURL+a.URLPath) + printInfoValue(logger, "ID", fmt.Sprintf("%d", a.ID)) + printInfoValue(logger, gotext.Get("Package Base ID"), fmt.Sprintf("%d", a.PackageBaseID)) + printInfoValue(logger, gotext.Get("Package Base"), a.PackageBase) + printInfoValue(logger, gotext.Get("Snapshot URL"), config.AURURL+a.URLPath) } - fmt.Println() + logger.Println() } // BiggestPackages prints the name of the ten biggest packages in the system. -func biggestPackages(dbExecutor db.Executor) { +func biggestPackages(logger *text.Logger, dbExecutor db.Executor) { pkgS := dbExecutor.BiggestPackages() if len(pkgS) < 10 { @@ -69,34 +74,34 @@ func biggestPackages(dbExecutor db.Executor) { } for i := 0; i < 10; i++ { - fmt.Printf("%s: %s\n", text.Bold(pkgS[i].Name()), text.Cyan(text.Human(pkgS[i].ISize()))) + logger.Printf("%s: %s\n", text.Bold(pkgS[i].Name()), text.Cyan(text.Human(pkgS[i].ISize()))) } } // localStatistics prints installed packages statistics. -func localStatistics(ctx context.Context, cfg *settings.Configuration, dbExecutor db.Executor) error { - info := statistics(cfg, dbExecutor) +func localStatistics(ctx context.Context, run *runtime.Runtime, dbExecutor db.Executor) error { + info := statistics(run, dbExecutor) remoteNames := dbExecutor.InstalledRemotePackageNames() remote := dbExecutor.InstalledRemotePackages() - 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)))) - text.Infoln(gotext.Get("Foreign installed packages: %s", text.Cyan(strconv.Itoa(len(remoteNames))))) - text.Infoln(gotext.Get("Explicitly installed packages: %s", text.Cyan(strconv.Itoa(info.Expln)))) - text.Infoln(gotext.Get("Total Size occupied by packages: %s", text.Cyan(text.Human(info.TotalSize)))) + run.Logger.Infoln(gotext.Get("Yay version v%s", yayVersion)) + run.Logger.Println(text.Bold(text.Cyan("==========================================="))) + run.Logger.Infoln(gotext.Get("Total installed packages: %s", text.Cyan(strconv.Itoa(info.Totaln)))) + run.Logger.Infoln(gotext.Get("Foreign installed packages: %s", text.Cyan(strconv.Itoa(len(remoteNames))))) + run.Logger.Infoln(gotext.Get("Explicitly installed packages: %s", text.Cyan(strconv.Itoa(info.Expln)))) + run.Logger.Infoln(gotext.Get("Total Size occupied by packages: %s", text.Cyan(text.Human(info.TotalSize)))) for path, size := range info.pacmanCaches { - text.Infoln(gotext.Get("Size of pacman cache %s: %s", path, text.Cyan(text.Human(size)))) + run.Logger.Infoln(gotext.Get("Size of pacman cache %s: %s", path, text.Cyan(text.Human(size)))) } - text.Infoln(gotext.Get("Size of yay cache %s: %s", cfg.BuildDir, text.Cyan(text.Human(info.yayCache)))) - fmt.Println(text.Bold(text.Cyan("==========================================="))) - text.Infoln(gotext.Get("Ten biggest packages:")) - biggestPackages(dbExecutor) - fmt.Println(text.Bold(text.Cyan("==========================================="))) + run.Logger.Infoln(gotext.Get("Size of yay cache %s: %s", run.Cfg.BuildDir, text.Cyan(text.Human(info.yayCache)))) + run.Logger.Println(text.Bold(text.Cyan("==========================================="))) + run.Logger.Infoln(gotext.Get("Ten biggest packages:")) + biggestPackages(run.Logger, dbExecutor) + run.Logger.Println(text.Bold(text.Cyan("==========================================="))) - aurData, err := cfg.Runtime.AURClient.Get(ctx, &aur.Query{ + aurData, err := run.AURClient.Get(ctx, &aur.Query{ Needles: remoteNames, By: aur.Name, }) @@ -104,7 +109,7 @@ func localStatistics(ctx context.Context, cfg *settings.Configuration, dbExecuto return err } - warnings := query.NewWarnings(cfg.Runtime.Logger.Child("print")) + warnings := query.NewWarnings(run.Logger.Child("warnings")) for i := range aurData { warnings.AddToWarnings(remote, &aurData[i]) } @@ -114,13 +119,13 @@ func localStatistics(ctx context.Context, cfg *settings.Configuration, dbExecuto return nil } -func printUpdateList(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments, +func printUpdateList(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor, enableDowngrade bool, filter upgrade.Filter, ) error { quietMode := cmdArgs.ExistsArg("q", "quiet") // TODO: handle quiet mode in a better way - logger := text.NewLogger(io.Discard, os.Stderr, os.Stdin, cfg.Debug, "update-list") + logger := text.NewLogger(io.Discard, os.Stderr, os.Stdin, run.Cfg.Debug, "update-list") dbExecutor.SetLogger(logger.Child("db")) oldNoConfirm := settings.NoConfirm settings.NoConfirm = true @@ -128,12 +133,12 @@ func printUpdateList(ctx context.Context, cfg *settings.Configuration, cmdArgs * defer func() { settings.NoConfirm = oldNoConfirm }() targets := mapset.NewThreadUnsafeSet(cmdArgs.Targets...) - grapher := dep.NewGrapher(dbExecutor, cfg.Runtime.AURClient, false, true, + grapher := dep.NewGrapher(dbExecutor, run.AURClient, false, true, false, false, cmdArgs.ExistsArg("needed"), logger.Child("grapher")) upService := upgrade.NewUpgradeService( - grapher, cfg.Runtime.AURClient, dbExecutor, cfg.Runtime.VCSStore, - cfg, true, logger.Child("upgrade")) + grapher, run.AURClient, dbExecutor, run.VCSStore, + run.Cfg, true, logger.Child("upgrade")) graph, errSysUp := upService.GraphUpgrades(ctx, nil, enableDowngrade, filter) @@ -163,9 +168,9 @@ func printUpdateList(ctx context.Context, cfg *settings.Configuration, cmdArgs * } if quietMode { - fmt.Printf("%s\n", pkgName) + run.Logger.Printf("%s\n", pkgName) } else { - fmt.Printf("%s %s -> %s\n", text.Bold(pkgName), text.Bold(text.Green(ii.LocalVersion)), + run.Logger.Printf("%s %s -> %s\n", text.Bold(pkgName), text.Bold(text.Green(ii.LocalVersion)), text.Bold(text.Green(ii.Version))) } @@ -179,7 +184,7 @@ func printUpdateList(ctx context.Context, cfg *settings.Configuration, cmdArgs * missing := false targets.Each(func(pkgName string) bool { if dbExecutor.LocalPackage(pkgName) == nil { - cfg.Runtime.Logger.Errorln(gotext.Get("package '%s' was not found", pkgName)) + run.Logger.Errorln(gotext.Get("package '%s' was not found", pkgName)) missing = true } return false @@ -191,3 +196,72 @@ func printUpdateList(ctx context.Context, cfg *settings.Configuration, cmdArgs * return nil } + +func printInfoValue(logger *text.Logger, key string, values ...string) { + const ( + keyLength = 32 + delimCount = 2 + ) + + specialWordsCount := 0 + + for _, runeValue := range key { + // CJK handling: the character 'ー' is Katakana + // but if use unicode.Katakana, it will return false + if unicode.IsOneOf([]*unicode.RangeTable{ + unicode.Han, + unicode.Hiragana, + unicode.Katakana, + unicode.Hangul, + }, runeValue) || runeValue == 'ー' { + specialWordsCount++ + } + } + + keyTextCount := specialWordsCount - keyLength + delimCount + str := fmt.Sprintf(text.Bold("%-*s: "), keyTextCount, key) + + if len(values) == 0 || (len(values) == 1 && values[0] == "") { + logger.Printf("%s%s\n", str, gotext.Get("None")) + return + } + + maxCols := getColumnCount() + cols := keyLength + len(values[0]) + str += values[0] + + for _, value := range values[1:] { + if maxCols > keyLength && cols+len(value)+delimCount >= maxCols { + cols = keyLength + str += "\n" + strings.Repeat(" ", keyLength) + } else if cols != keyLength { + str += strings.Repeat(" ", delimCount) + cols += delimCount + } + + str += value + cols += len(value) + } + + logger.Println(str) +} + +var cachedColumnCount = -1 + +func getColumnCount() int { + if cachedColumnCount > 0 { + return cachedColumnCount + } + + if count, err := strconv.Atoi(os.Getenv("COLUMNS")); err == nil { + cachedColumnCount = count + return cachedColumnCount + } + + if ws, err := unix.IoctlGetWinsize(syscall.Stdout, unix.TIOCGWINSZ); err == nil { + cachedColumnCount = int(ws.Col) + return cachedColumnCount + } + + return 80 +} diff --git a/print_test.go b/print_test.go index 4366345..f7b5ee2 100644 --- a/print_test.go +++ b/print_test.go @@ -20,6 +20,7 @@ import ( "github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/db/mock" mockaur "github.com/Jguer/yay/v12/pkg/dep/mock" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/parser" @@ -271,29 +272,28 @@ func TestPrintUpdateList(t *testing.T) { SudoLoopEnabled: false, } - cfg := &settings.Configuration{ - RemoveMake: "no", - Runtime: &settings.Runtime{ - Logger: NewTestLogger(), - CmdBuilder: cmdBuilder, - VCSStore: &vcs.Mock{}, - AURClient: tc.mockData.aurCache, + r, w, _ := os.Pipe() + + logger := text.NewLogger(w, io.Discard, strings.NewReader(""), true, "test") + + run := &runtime.Runtime{ + Cfg: &settings.Configuration{ + RemoveMake: "no", }, + Logger: logger, + CmdBuilder: cmdBuilder, + VCSStore: &vcs.Mock{}, + AURClient: tc.mockData.aurCache, } cmdArgs := parser.MakeArguments() cmdArgs.AddArg(tc.args...) cmdArgs.AddTarget(tc.targets...) - rescueStdout := os.Stdout - r, w, _ := os.Pipe() - os.Stdout = w - - err = handleCmd(context.Background(), cfg, cmdArgs, tc.mockData.db) + err = handleCmd(context.Background(), run, cmdArgs, tc.mockData.db) w.Close() out, _ := io.ReadAll(r) - os.Stdout = rescueStdout if tc.wantErr { require.Error(t, err) diff --git a/query.go b/query.go index 1c9af66..497319b 100644 --- a/query.go +++ b/query.go @@ -12,6 +12,7 @@ import ( "github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/query" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/text" @@ -32,7 +33,7 @@ func syncSearch(ctx context.Context, pkgS []string, } // SyncInfo serves as a pacman -Si for repo packages and AUR packages. -func syncInfo(ctx context.Context, cfg *settings.Configuration, +func syncInfo(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, pkgS []string, dbExecutor db.Executor, ) error { var ( @@ -41,8 +42,8 @@ func syncInfo(ctx context.Context, cfg *settings.Configuration, missing = false ) - pkgS = query.RemoveInvalidTargets(pkgS, cfg.Mode) - aurS, repoS := packageSlices(pkgS, cfg, dbExecutor) + pkgS = query.RemoveInvalidTargets(run.Logger, pkgS, run.Cfg.Mode) + aurS, repoS := packageSlices(pkgS, run.Cfg, dbExecutor) if len(aurS) != 0 { noDB := make([]string, 0, len(aurS)) @@ -52,14 +53,14 @@ func syncInfo(ctx context.Context, cfg *settings.Configuration, noDB = append(noDB, name) } - info, err = cfg.Runtime.AURClient.Get(ctx, &aur.Query{ + info, err = run.AURClient.Get(ctx, &aur.Query{ Needles: noDB, By: aur.Name, }) if err != nil { missing = true - cfg.Runtime.Logger.Errorln(err) + run.Logger.Errorln(err) } } @@ -68,8 +69,8 @@ func syncInfo(ctx context.Context, cfg *settings.Configuration, arguments.ClearTargets() arguments.AddTarget(repoS...) - err = cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx, - arguments, cfg.Mode, settings.NoConfirm)) + err = run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, + arguments, run.Cfg.Mode, settings.NoConfirm)) if err != nil { return err } @@ -81,7 +82,7 @@ func syncInfo(ctx context.Context, cfg *settings.Configuration, if len(info) != 0 { for i := range info { - printInfo(cfg, &info[i], cmdArgs.ExistsDouble("i")) + printInfo(run.Logger, run.Cfg, &info[i], cmdArgs.ExistsDouble("i")) } } @@ -220,7 +221,7 @@ func getFolderSize(path string) (size int64) { } // Statistics returns statistics about packages installed in system. -func statistics(cfg *settings.Configuration, dbExecutor db.Executor) (res struct { +func statistics(run *runtime.Runtime, dbExecutor db.Executor) (res struct { Totaln int Expln int TotalSize int64 @@ -238,11 +239,11 @@ func statistics(cfg *settings.Configuration, dbExecutor db.Executor) (res struct } res.pacmanCaches = make(map[string]int64) - for _, path := range cfg.Runtime.PacmanConf.CacheDir { + for _, path := range run.PacmanConf.CacheDir { res.pacmanCaches[path] = getFolderSize(path) } - res.yayCache = getFolderSize(cfg.BuildDir) + res.yayCache = getFolderSize(run.Cfg.BuildDir) return } diff --git a/query_test.go b/query_test.go index e57fb7c..11231a9 100644 --- a/query_test.go +++ b/query_test.go @@ -16,6 +16,7 @@ import ( "github.com/Jguer/yay/v12/pkg/db/mock" mockaur "github.com/Jguer/yay/v12/pkg/dep/mock" "github.com/Jguer/yay/v12/pkg/query" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/parser" @@ -125,12 +126,12 @@ func TestSyncInfo(t *testing.T) { Runner: mockRunner, SudoLoopEnabled: false, } - cfg := &settings.Configuration{ - Runtime: &settings.Runtime{ - CmdBuilder: cmdBuilder, - AURClient: mockAUR, - Logger: NewTestLogger(), - }, + + run := &runtime.Runtime{ + CmdBuilder: cmdBuilder, + AURClient: mockAUR, + Logger: newTestLogger(), + Cfg: &settings.Configuration{}, } cmdArgs := parser.MakeArguments() @@ -138,7 +139,7 @@ func TestSyncInfo(t *testing.T) { cmdArgs.AddTarget(tc.targets...) err := handleCmd(context.Background(), - cfg, cmdArgs, dbExc, + run, cmdArgs, dbExc, ) if tc.wantErr { @@ -266,14 +267,14 @@ func TestSyncSearchAURDB(t *testing.T) { Runner: mockRunner, SudoLoopEnabled: false, } - cfg := &settings.Configuration{ - Runtime: &settings.Runtime{ - CmdBuilder: cmdBuilder, - AURClient: mockAUR, - QueryBuilder: query.NewSourceQueryBuilder(mockAUR, NewTestLogger(), "votes", parser.ModeAny, "name", - tc.bottomUp, tc.singleLine, tc.mixed), - Logger: NewTestLogger(), - }, + + run := &runtime.Runtime{ + CmdBuilder: cmdBuilder, + AURClient: mockAUR, + QueryBuilder: query.NewSourceQueryBuilder(mockAUR, newTestLogger(), "votes", parser.ModeAny, "name", + tc.bottomUp, tc.singleLine, tc.mixed), + Logger: newTestLogger(), + Cfg: &settings.Configuration{}, } cmdArgs := parser.MakeArguments() @@ -281,7 +282,7 @@ func TestSyncSearchAURDB(t *testing.T) { cmdArgs.AddTarget(tc.targets...) err := handleCmd(context.Background(), - cfg, cmdArgs, dbExc, + run, cmdArgs, dbExc, ) if tc.wantErr { diff --git a/sync.go b/sync.go index 862455c..53a5013 100644 --- a/sync.go +++ b/sync.go @@ -3,37 +3,36 @@ package main import ( "context" "fmt" - "os" "strings" - "github.com/Jguer/yay/v12/pkg/completion" + "github.com/leonelquinteros/gotext" + "github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/dep" "github.com/Jguer/yay/v12/pkg/multierror" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" + "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/parser" - "github.com/Jguer/yay/v12/pkg/srcinfo" - "github.com/Jguer/yay/v12/pkg/text" + "github.com/Jguer/yay/v12/pkg/sync" "github.com/Jguer/yay/v12/pkg/upgrade" - - "github.com/leonelquinteros/gotext" ) func syncInstall(ctx context.Context, - cfg *settings.Configuration, + run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor, ) error { - aurCache := cfg.Runtime.AURClient + aurCache := run.AURClient refreshArg := cmdArgs.ExistsArg("y", "refresh") noDeps := cmdArgs.ExistsArg("d", "nodeps") - noCheck := strings.Contains(cfg.MFlags, "--nocheck") + noCheck := strings.Contains(run.Cfg.MFlags, "--nocheck") if noDeps { - cfg.Runtime.CmdBuilder.AddMakepkgFlag("-d") + run.CmdBuilder.AddMakepkgFlag("-d") } - if refreshArg && cfg.Mode.AtLeastRepo() { - if errR := earlyRefresh(ctx, cfg, cfg.Runtime.CmdBuilder, cmdArgs); errR != nil { + if refreshArg && run.Cfg.Mode.AtLeastRepo() { + if errR := earlyRefresh(ctx, run.Cfg, run.CmdBuilder, cmdArgs); errR != nil { return fmt.Errorf("%s - %w", gotext.Get("error refreshing databases"), errR) } @@ -45,7 +44,7 @@ func syncInstall(ctx context.Context, } grapher := dep.NewGrapher(dbExecutor, aurCache, false, settings.NoConfirm, - noDeps, noCheck, cmdArgs.ExistsArg("needed"), cfg.Runtime.Logger.Child("grapher")) + noDeps, noCheck, cmdArgs.ExistsArg("needed"), run.Logger.Child("grapher")) graph, err := grapher.GraphFromTargets(ctx, nil, cmdArgs.Targets) if err != nil { @@ -57,8 +56,8 @@ func syncInstall(ctx context.Context, var errSysUp error upService := upgrade.NewUpgradeService( - grapher, aurCache, dbExecutor, cfg.Runtime.VCSStore, - cfg, settings.NoConfirm, cfg.Runtime.Logger.Child("upgrade")) + grapher, aurCache, dbExecutor, run.VCSStore, + run.Cfg, settings.NoConfirm, run.Logger.Child("upgrade")) graph, errSysUp = upService.GraphUpgrades(ctx, graph, cmdArgs.ExistsDouble("u", "sysupgrade"), @@ -75,7 +74,7 @@ func syncInstall(ctx context.Context, } } - opService := NewOperationService(ctx, cfg, dbExecutor) + opService := sync.NewOperationService(ctx, dbExecutor, run) multiErr := &multierror.MultiError{} targets := graph.TopoSortedLayerMap(func(s string, ii *dep.InstallInfo) error { if ii.Source == dep.Missing { @@ -88,117 +87,19 @@ func syncInstall(ctx context.Context, return err } - return opService.Run(ctx, cmdArgs, targets, excluded) + return opService.Run(ctx, run, cmdArgs, targets, excluded) } -type OperationService struct { - ctx context.Context - cfg *settings.Configuration - dbExecutor db.Executor -} - -func NewOperationService(ctx context.Context, cfg *settings.Configuration, dbExecutor db.Executor) *OperationService { - return &OperationService{ - ctx: ctx, - cfg: cfg, - dbExecutor: dbExecutor, - } -} - -func (o *OperationService) Run(ctx context.Context, - cmdArgs *parser.Arguments, - targets []map[string]*dep.InstallInfo, excluded []string, -) error { - if len(targets) == 0 { - fmt.Fprintln(os.Stdout, "", gotext.Get("there is nothing to do")) - return nil - } - preparer := NewPreparer(o.dbExecutor, o.cfg.Runtime.CmdBuilder, o.cfg) - installer := NewInstaller(o.dbExecutor, o.cfg.Runtime.CmdBuilder, - o.cfg.Runtime.VCSStore, o.cfg.Mode, o.cfg.ReBuild, - cmdArgs.ExistsArg("w", "downloadonly"), o.cfg.Runtime.Logger.Child("installer")) - - pkgBuildDirs, errInstall := preparer.Run(ctx, os.Stdout, targets) - if errInstall != nil { - return errInstall - } - - if cleanFunc := preparer.ShouldCleanMakeDeps(cmdArgs); cleanFunc != nil { - installer.AddPostInstallHook(cleanFunc) - } - - if cleanAURDirsFunc := preparer.ShouldCleanAURDirs(pkgBuildDirs); cleanAURDirsFunc != nil { - installer.AddPostInstallHook(cleanAURDirsFunc) - } - - go func() { - errComp := completion.Update(ctx, o.cfg.Runtime.HTTPClient, o.dbExecutor, - o.cfg.AURURL, o.cfg.CompletionPath, o.cfg.CompletionInterval, false) - if errComp != nil { - text.Warnln(errComp) - } - }() - - srcInfo, errInstall := srcinfo.NewService(o.dbExecutor, o.cfg, o.cfg.Runtime.CmdBuilder, o.cfg.Runtime.VCSStore, pkgBuildDirs) - if errInstall != nil { - return errInstall - } - - incompatible, errInstall := srcInfo.IncompatiblePkgs(ctx) - if errInstall != nil { - return errInstall - } - - if errIncompatible := confirmIncompatible(incompatible); errIncompatible != nil { - return errIncompatible - } - - if errPGP := srcInfo.CheckPGPKeys(ctx); errPGP != nil { - return errPGP - } - - if errInstall := installer.Install(ctx, cmdArgs, targets, pkgBuildDirs, - excluded, o.manualConfirmRequired(cmdArgs)); errInstall != nil { - return errInstall - } - - var multiErr multierror.MultiError - - if err := installer.CompileFailedAndIgnored(); err != nil { - multiErr.Add(err) - } - - if !cmdArgs.ExistsArg("w", "downloadonly") { - if err := srcInfo.UpdateVCSStore(ctx, targets, installer.failedAndIgnored); err != nil { - text.Warnln(err) - } - } - - if err := installer.RunPostInstallHooks(ctx); err != nil { - multiErr.Add(err) - } - - return multiErr.Return() -} - -func (o *OperationService) manualConfirmRequired(cmdArgs *parser.Arguments) bool { - return (!cmdArgs.ExistsArg("u", "sysupgrade") && cmdArgs.Op != "Y") || o.cfg.DoubleConfirm -} - -func confirmIncompatible(incompatible []string) error { - if len(incompatible) > 0 { - text.Warnln(gotext.Get("The following packages are not compatible with your architecture:")) - - for _, pkg := range incompatible { - fmt.Print(" " + text.Cyan(pkg)) - } - - fmt.Println() - - if !text.ContinueTask(os.Stdin, gotext.Get("Try to build them anyway?"), true, settings.NoConfirm) { - return &settings.ErrUserAbort{} - } - } - - return nil +func earlyRefresh(ctx context.Context, cfg *settings.Configuration, cmdBuilder exe.ICmdBuilder, cmdArgs *parser.Arguments) error { + arguments := cmdArgs.Copy() + if cfg.CombinedUpgrade { + arguments.DelArg("u", "sysupgrade") + } + arguments.DelArg("s", "search") + arguments.DelArg("i", "info") + arguments.DelArg("l", "list") + arguments.ClearTargets() + + return cmdBuilder.Show(cmdBuilder.BuildPacmanCmd(ctx, + arguments, cfg.Mode, settings.NoConfirm)) } diff --git a/sync_test.go b/sync_test.go index 8e2331f..973b16e 100644 --- a/sync_test.go +++ b/sync_test.go @@ -23,6 +23,7 @@ import ( "github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/db/mock" mockaur "github.com/Jguer/yay/v12/pkg/dep/mock" + "github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/parser" @@ -106,21 +107,20 @@ func TestSyncUpgrade(t *testing.T) { }, } - cfg := &settings.Configuration{ - RemoveMake: "no", - Runtime: &settings.Runtime{ - Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("\n"), true, "test"), - CmdBuilder: cmdBuilder, - VCSStore: &vcs.Mock{}, - AURClient: &mockaur.MockAUR{ - GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { - return []aur.Pkg{}, nil - }, + run := &runtime.Runtime{ + Cfg: &settings.Configuration{ + RemoveMake: "no", + }, + Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("\n"), true, "test"), + CmdBuilder: cmdBuilder, + VCSStore: &vcs.Mock{}, + AURClient: &mockaur.MockAUR{ + GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { + return []aur.Pkg{}, nil }, }, } - - err = handleCmd(context.Background(), cfg, cmdArgs, db) + err = handleCmd(context.Background(), run, cmdArgs, db) require.NoError(t, err) wantCapture := []string{} @@ -219,21 +219,20 @@ func TestSyncUpgrade_IgnoreAll(t *testing.T) { }, } - cfg := &settings.Configuration{ - RemoveMake: "no", - Runtime: &settings.Runtime{ - Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test"), - CmdBuilder: cmdBuilder, - VCSStore: &vcs.Mock{}, - AURClient: &mockaur.MockAUR{ - GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { - return []aur.Pkg{}, nil - }, + run := &runtime.Runtime{ + Cfg: &settings.Configuration{ + RemoveMake: "no", + }, + Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test"), + CmdBuilder: cmdBuilder, + VCSStore: &vcs.Mock{}, + AURClient: &mockaur.MockAUR{ + GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { + return []aur.Pkg{}, nil }, }, } - - err = handleCmd(context.Background(), cfg, cmdArgs, db) + err = handleCmd(context.Background(), run, cmdArgs, db) require.NoError(t, err) wantCapture := []string{} @@ -349,21 +348,21 @@ func TestSyncUpgrade_IgnoreOne(t *testing.T) { }, } - cfg := &settings.Configuration{ - RemoveMake: "no", - Runtime: &settings.Runtime{ - Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test"), - CmdBuilder: cmdBuilder, - VCSStore: &vcs.Mock{}, - AURClient: &mockaur.MockAUR{ - GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { - return []aur.Pkg{}, nil - }, + run := &runtime.Runtime{ + Cfg: &settings.Configuration{ + RemoveMake: "no", + }, + Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test"), + CmdBuilder: cmdBuilder, + VCSStore: &vcs.Mock{}, + AURClient: &mockaur.MockAUR{ + GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { + return []aur.Pkg{}, nil }, }, } - err = handleCmd(context.Background(), cfg, cmdArgs, db) + err = handleCmd(context.Background(), run, cmdArgs, db) require.NoError(t, err) wantCapture := []string{} @@ -512,37 +511,37 @@ pkgname = python-vosk }, } - cfg := &settings.Configuration{ - DoubleConfirm: true, - RemoveMake: "no", - BuildDir: tmpDir, - Runtime: &settings.Runtime{ - Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("\n\n\n\n"), true, "test"), - CmdBuilder: cmdBuilder, - VCSStore: &vcs.Mock{}, - AURClient: &mockaur.MockAUR{ - GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { - return []aur.Pkg{ - { - Name: "vosk-api", - PackageBase: "vosk-api", - Version: "0.3.45-1", + run := &runtime.Runtime{ + Cfg: &settings.Configuration{ + DoubleConfirm: true, + RemoveMake: "no", + BuildDir: tmpDir, + }, + Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("\n\n\n\n"), true, "test"), + CmdBuilder: cmdBuilder, + VCSStore: &vcs.Mock{}, + AURClient: &mockaur.MockAUR{ + GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { + return []aur.Pkg{ + { + Name: "vosk-api", + PackageBase: "vosk-api", + Version: "0.3.45-1", + }, + { + Name: "python-vosk", + PackageBase: "vosk-api", + Version: "0.3.45-1", + Depends: []string{ + "vosk-api=0.3.45", }, - { - Name: "python-vosk", - PackageBase: "vosk-api", - Version: "0.3.45-1", - Depends: []string{ - "vosk-api=0.3.45", - }, - }, - }, nil - }, + }, + }, nil }, }, } - err = handleCmd(context.Background(), cfg, cmdArgs, db) + err = handleCmd(context.Background(), run, cmdArgs, db) require.NoError(t, err) wantCapture := []string{ @@ -697,22 +696,22 @@ func TestSyncUpgrade_NoCombinedUpgrade(t *testing.T) { }, } - cfg := &settings.Configuration{ - RemoveMake: "no", - CombinedUpgrade: false, - Runtime: &settings.Runtime{ - Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test"), - CmdBuilder: cmdBuilder, - VCSStore: &vcs.Mock{}, - AURClient: &mockaur.MockAUR{ - GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { - return []aur.Pkg{}, nil - }, + run := &runtime.Runtime{ + Cfg: &settings.Configuration{ + RemoveMake: "no", + CombinedUpgrade: false, + }, + Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test"), + CmdBuilder: cmdBuilder, + VCSStore: &vcs.Mock{}, + AURClient: &mockaur.MockAUR{ + GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { + return []aur.Pkg{}, nil }, }, } - err = handleCmd(context.Background(), cfg, cmdArgs, db) + err = handleCmd(context.Background(), run, cmdArgs, db) require.NoError(t, err) require.Len(t, mockRunner.ShowCalls, len(tc.want)) diff --git a/vcs.go b/vcs.go index 230aa33..981b53f 100644 --- a/vcs.go +++ b/vcs.go @@ -2,7 +2,6 @@ package main import ( "context" - "os" "sync" "github.com/Jguer/aur" @@ -10,9 +9,9 @@ import ( "github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/dep" - "github.com/Jguer/yay/v12/pkg/settings" - "github.com/Jguer/yay/v12/pkg/srcinfo" - "github.com/Jguer/yay/v12/pkg/text" + "github.com/Jguer/yay/v12/pkg/runtime" + "github.com/Jguer/yay/v12/pkg/sync/srcinfo" + "github.com/Jguer/yay/v12/pkg/sync/workdir" ) func infoToInstallInfo(info []aur.Pkg) []map[string]*dep.InstallInfo { @@ -31,11 +30,11 @@ func infoToInstallInfo(info []aur.Pkg) []map[string]*dep.InstallInfo { } // createDevelDB forces yay to create a DB of the existing development packages. -func createDevelDB(ctx context.Context, cfg *settings.Configuration, dbExecutor db.Executor) error { +func createDevelDB(ctx context.Context, run *runtime.Runtime, dbExecutor db.Executor) error { remoteNames := dbExecutor.InstalledRemotePackageNames() - cfg.Runtime.QueryBuilder.Execute(ctx, dbExecutor, remoteNames) - info, err := cfg.Runtime.AURClient.Get(ctx, &aur.Query{ + run.QueryBuilder.Execute(ctx, dbExecutor, remoteNames) + info, err := run.AURClient.Get(ctx, &aur.Query{ Needles: remoteNames, By: aur.Name, Contains: false, @@ -44,15 +43,15 @@ func createDevelDB(ctx context.Context, cfg *settings.Configuration, dbExecutor return err } - preper := NewPreparerWithoutHooks(dbExecutor, cfg.Runtime.CmdBuilder, cfg, false) + preper := workdir.NewPreparerWithoutHooks(dbExecutor, run.CmdBuilder, run.Cfg, run.Logger.Child("workdir"), false) mapInfo := infoToInstallInfo(info) - pkgBuildDirsByBase, err := preper.Run(ctx, os.Stdout, mapInfo) + pkgBuildDirsByBase, err := preper.Run(ctx, run, mapInfo) if err != nil { return err } - srcinfos, err := srcinfo.ParseSrcinfoFilesByBase(pkgBuildDirsByBase, false) + srcinfos, err := srcinfo.ParseSrcinfoFilesByBase(run.Logger.Child("srcinfo"), pkgBuildDirsByBase, false) if err != nil { return err } @@ -63,14 +62,14 @@ func createDevelDB(ctx context.Context, cfg *settings.Configuration, dbExecutor wg.Add(1) go func(i string, iP int) { - cfg.Runtime.VCSStore.Update(ctx, srcinfos[i].Packages[iP].Pkgname, srcinfos[i].Source) + run.VCSStore.Update(ctx, srcinfos[i].Packages[iP].Pkgname, srcinfos[i].Source) wg.Done() }(i, iP) } } wg.Wait() - text.OperationInfoln(gotext.Get("GenDB finished. No packages were installed")) + run.Logger.OperationInfoln(gotext.Get("GenDB finished. No packages were installed")) return err } diff --git a/vote.go b/vote.go index 19e7868..2ed29da 100644 --- a/vote.go +++ b/vote.go @@ -3,11 +3,12 @@ package main import ( "context" "errors" - "fmt" "github.com/Jguer/aur" "github.com/Jguer/votar/pkg/vote" "github.com/leonelquinteros/gotext" + + "github.com/Jguer/yay/v12/pkg/text" ) type ErrAURVote struct { @@ -20,7 +21,7 @@ func (e *ErrAURVote) Error() string { } func handlePackageVote(ctx context.Context, - targets []string, aurClient aur.QueryClient, + targets []string, aurClient aur.QueryClient, logger *text.Logger, voteClient *vote.Client, upvote bool, ) error { infos, err := aurClient.Get(ctx, &aur.Query{ @@ -32,7 +33,7 @@ func handlePackageVote(ctx context.Context, } if len(infos) == 0 { - fmt.Println(gotext.Get(" there is nothing to do")) + logger.Println(gotext.Get(" there is nothing to do")) return nil }