From fe14d56afebc7400a27ca8728c1b49591da103ac Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Tue, 19 Oct 2021 22:26:23 +0200 Subject: [PATCH] Massive UX rewrite - Ditch multiple libraries for progressbar, spinner, colors and replace with pterm - Detect when running on terminal and disable automatically spinner - Add support for multiple progress bars - Huge rewrite of the configuration part. No more crazy stuff with viper CLI commands now correctly overrides default config file as expected - Limit banner to be displayed on relevant parts Fixes #211 Fixes #105 Fixes #247 Fixes #233 --- cmd/build.go | 67 +++--- cmd/helpers/cli_suite_test.go | 5 +- cmd/install.go | 17 +- cmd/reclaim.go | 5 +- cmd/reinstall.go | 15 +- cmd/replace.go | 21 +- cmd/repo/update.go | 4 - cmd/root.go | 148 +----------- cmd/search.go | 5 +- cmd/tree/validate.go | 2 +- cmd/uninstall.go | 11 +- cmd/upgrade.go | 9 +- cmd/util/cli.go | 45 ++-- cmd/util/config.go | 217 ++++++++++++++++++ go.mod | 8 +- go.sum | 46 ++-- .../types/artifact/artifact_suite_test.go | 5 +- pkg/api/core/types/config_suite_test.go | 5 +- pkg/compiler/backend/backend_suite_test.go | 5 +- pkg/compiler/backend/common.go | 2 +- pkg/compiler/compiler.go | 5 +- pkg/compiler/compiler_suite_test.go | 5 +- pkg/compiler/types/spec/spec_suite_test.go | 5 +- pkg/config/config.go | 62 +---- pkg/config/config_suite_test.go | 5 +- pkg/helpers/helpers_suite_test.go | 5 +- pkg/helpers/terminal/terminal_check_bsd.go | 12 + .../terminal/terminal_check_general.go | 17 ++ .../terminal/terminal_check_no_terminal.go | 11 + .../terminal/terminal_check_solaris.go | 11 + pkg/helpers/terminal/terminal_check_unix.go | 12 + .../terminal/terminal_check_windows.go | 27 +++ pkg/installer/cli.go | 97 ++++++++ pkg/installer/client/client_suite_test.go | 4 +- pkg/installer/client/http.go | 43 ++-- pkg/installer/installer.go | 98 ++++---- pkg/installer/installer_suite_test.go | 4 +- pkg/logger/logger.go | 95 ++++++-- pkg/package/package_suite_test.go | 5 +- pkg/repository/repository_suite_test.go | 5 +- pkg/repository/repository_test.go | 3 +- pkg/solver/solver_suite_test.go | 5 +- pkg/spectooling/spectooling_suite_test.go | 5 +- pkg/tree/tree_suite_test.go | 5 +- pkg/versioner/versioner_suite_test.go | 5 +- 45 files changed, 730 insertions(+), 463 deletions(-) create mode 100644 cmd/util/config.go create mode 100644 pkg/helpers/terminal/terminal_check_bsd.go create mode 100644 pkg/helpers/terminal/terminal_check_general.go create mode 100644 pkg/helpers/terminal/terminal_check_no_terminal.go create mode 100644 pkg/helpers/terminal/terminal_check_solaris.go create mode 100644 pkg/helpers/terminal/terminal_check_unix.go create mode 100644 pkg/helpers/terminal/terminal_check_windows.go create mode 100644 pkg/installer/cli.go diff --git a/cmd/build.go b/cmd/build.go index 2fd03e95..5dece17f 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -36,6 +36,7 @@ import ( tree "github.com/mudler/luet/pkg/tree" "github.com/spf13/cobra" + "github.com/spf13/viper" ) var buildCmd = &cobra.Command{ @@ -65,54 +66,54 @@ Build packages specifying multiple definition trees: $ luet build --tree overlay/path --tree overlay/path2 utils/yq ... `, PreRun: func(cmd *cobra.Command, args []string) { - LuetCfg.Viper.BindPFlag("tree", cmd.Flags().Lookup("tree")) - LuetCfg.Viper.BindPFlag("destination", cmd.Flags().Lookup("destination")) - LuetCfg.Viper.BindPFlag("backend", cmd.Flags().Lookup("backend")) - LuetCfg.Viper.BindPFlag("privileged", cmd.Flags().Lookup("privileged")) - LuetCfg.Viper.BindPFlag("revdeps", cmd.Flags().Lookup("revdeps")) - LuetCfg.Viper.BindPFlag("all", cmd.Flags().Lookup("all")) - LuetCfg.Viper.BindPFlag("compression", cmd.Flags().Lookup("compression")) - LuetCfg.Viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps")) - LuetCfg.Viper.BindPFlag("onlydeps", cmd.Flags().Lookup("onlydeps")) + viper.BindPFlag("tree", cmd.Flags().Lookup("tree")) + viper.BindPFlag("destination", cmd.Flags().Lookup("destination")) + viper.BindPFlag("backend", cmd.Flags().Lookup("backend")) + viper.BindPFlag("privileged", cmd.Flags().Lookup("privileged")) + viper.BindPFlag("revdeps", cmd.Flags().Lookup("revdeps")) + viper.BindPFlag("all", cmd.Flags().Lookup("all")) + viper.BindPFlag("compression", cmd.Flags().Lookup("compression")) + viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps")) + viper.BindPFlag("onlydeps", cmd.Flags().Lookup("onlydeps")) util.BindValuesFlags(cmd) - LuetCfg.Viper.BindPFlag("backend-args", cmd.Flags().Lookup("backend-args")) + viper.BindPFlag("backend-args", cmd.Flags().Lookup("backend-args")) - LuetCfg.Viper.BindPFlag("image-repository", cmd.Flags().Lookup("image-repository")) - LuetCfg.Viper.BindPFlag("push", cmd.Flags().Lookup("push")) - LuetCfg.Viper.BindPFlag("pull", cmd.Flags().Lookup("pull")) - LuetCfg.Viper.BindPFlag("wait", cmd.Flags().Lookup("wait")) - LuetCfg.Viper.BindPFlag("keep-images", cmd.Flags().Lookup("keep-images")) + viper.BindPFlag("image-repository", cmd.Flags().Lookup("image-repository")) + viper.BindPFlag("push", cmd.Flags().Lookup("push")) + viper.BindPFlag("pull", cmd.Flags().Lookup("pull")) + viper.BindPFlag("wait", cmd.Flags().Lookup("wait")) + viper.BindPFlag("keep-images", cmd.Flags().Lookup("keep-images")) util.BindSolverFlags(cmd) - LuetCfg.Viper.BindPFlag("general.show_build_output", cmd.Flags().Lookup("live-output")) - LuetCfg.Viper.BindPFlag("backend-args", cmd.Flags().Lookup("backend-args")) + viper.BindPFlag("general.show_build_output", cmd.Flags().Lookup("live-output")) + viper.BindPFlag("backend-args", cmd.Flags().Lookup("backend-args")) }, Run: func(cmd *cobra.Command, args []string) { - treePaths := LuetCfg.Viper.GetStringSlice("tree") - dst := LuetCfg.Viper.GetString("destination") + treePaths := viper.GetStringSlice("tree") + dst := viper.GetString("destination") concurrency := LuetCfg.GetGeneral().Concurrency - backendType := LuetCfg.Viper.GetString("backend") - privileged := LuetCfg.Viper.GetBool("privileged") - revdeps := LuetCfg.Viper.GetBool("revdeps") - all := LuetCfg.Viper.GetBool("all") - compressionType := LuetCfg.Viper.GetString("compression") - imageRepository := LuetCfg.Viper.GetString("image-repository") + backendType := viper.GetString("backend") + privileged := viper.GetBool("privileged") + revdeps := viper.GetBool("revdeps") + all := viper.GetBool("all") + compressionType := viper.GetString("compression") + imageRepository := viper.GetString("image-repository") values := util.ValuesFlags() - wait := LuetCfg.Viper.GetBool("wait") - push := LuetCfg.Viper.GetBool("push") - pull := LuetCfg.Viper.GetBool("pull") - keepImages := LuetCfg.Viper.GetBool("keep-images") - nodeps := LuetCfg.Viper.GetBool("nodeps") - onlydeps := LuetCfg.Viper.GetBool("onlydeps") + wait := viper.GetBool("wait") + push := viper.GetBool("push") + pull := viper.GetBool("pull") + keepImages := viper.GetBool("keep-images") + nodeps := viper.GetBool("nodeps") + onlydeps := viper.GetBool("onlydeps") onlyTarget, _ := cmd.Flags().GetBool("only-target-package") full, _ := cmd.Flags().GetBool("full") rebuild, _ := cmd.Flags().GetBool("rebuild") var results Results - backendArgs := LuetCfg.Viper.GetStringSlice("backend-args") + backendArgs := viper.GetStringSlice("backend-args") out, _ := cmd.Flags().GetString("output") if out != "terminal" { @@ -148,7 +149,7 @@ Build packages specifying multiple definition trees: opts := util.SetSolverConfig() pullRepo, _ := cmd.Flags().GetStringArray("pull-repository") - LuetCfg.GetGeneral().ShowBuildOutput = LuetCfg.Viper.GetBool("general.show_build_output") + LuetCfg.GetGeneral().ShowBuildOutput = viper.GetBool("general.show_build_output") Debug("Solver", opts.CompactString()) diff --git a/cmd/helpers/cli_suite_test.go b/cmd/helpers/cli_suite_test.go index ba088f9e..8cdf72be 100644 --- a/cmd/helpers/cli_suite_test.go +++ b/cmd/helpers/cli_suite_test.go @@ -18,8 +18,7 @@ package cmd_helpers_test import ( "testing" - . "github.com/mudler/luet/cmd" - config "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/cmd/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -27,6 +26,6 @@ import ( func TestSolver(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() RunSpecs(t, "CLI helpers test Suite") } diff --git a/cmd/install.go b/cmd/install.go index b799e348..cecff51b 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -25,6 +25,7 @@ import ( pkg "github.com/mudler/luet/pkg/package" "github.com/spf13/cobra" + "github.com/spf13/viper" ) var installCmd = &cobra.Command{ @@ -50,10 +51,10 @@ To force install a package: PreRun: func(cmd *cobra.Command, args []string) { util.BindSystemFlags(cmd) util.BindSolverFlags(cmd) - LuetCfg.Viper.BindPFlag("onlydeps", cmd.Flags().Lookup("onlydeps")) - LuetCfg.Viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps")) - LuetCfg.Viper.BindPFlag("force", cmd.Flags().Lookup("force")) - LuetCfg.Viper.BindPFlag("yes", cmd.Flags().Lookup("yes")) + viper.BindPFlag("onlydeps", cmd.Flags().Lookup("onlydeps")) + viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps")) + viper.BindPFlag("force", cmd.Flags().Lookup("force")) + viper.BindPFlag("yes", cmd.Flags().Lookup("yes")) }, Run: func(cmd *cobra.Command, args []string) { var toInstall pkg.Packages @@ -66,10 +67,10 @@ To force install a package: toInstall = append(toInstall, pack) } - force := LuetCfg.Viper.GetBool("force") - nodeps := LuetCfg.Viper.GetBool("nodeps") - onlydeps := LuetCfg.Viper.GetBool("onlydeps") - yes := LuetCfg.Viper.GetBool("yes") + force := viper.GetBool("force") + nodeps := viper.GetBool("nodeps") + onlydeps := viper.GetBool("onlydeps") + yes := viper.GetBool("yes") downloadOnly, _ := cmd.Flags().GetBool("download-only") finalizerEnvs, _ := cmd.Flags().GetStringArray("finalizer-env") relax, _ := cmd.Flags().GetBool("relax") diff --git a/cmd/reclaim.go b/cmd/reclaim.go index b549c1dd..48a67e8a 100644 --- a/cmd/reclaim.go +++ b/cmd/reclaim.go @@ -22,6 +22,7 @@ import ( . "github.com/mudler/luet/pkg/logger" "github.com/spf13/cobra" + "github.com/spf13/viper" ) var reclaimCmd = &cobra.Command{ @@ -29,7 +30,7 @@ var reclaimCmd = &cobra.Command{ Short: "Reclaim packages to Luet database from available repositories", PreRun: func(cmd *cobra.Command, args []string) { util.BindSystemFlags(cmd) - LuetCfg.Viper.BindPFlag("force", cmd.Flags().Lookup("force")) + viper.BindPFlag("force", cmd.Flags().Lookup("force")) }, Long: `Reclaim tries to find association between packages in the online repositories and the system one. @@ -40,7 +41,7 @@ It scans the target file system, and if finds a match with a package available i Run: func(cmd *cobra.Command, args []string) { util.SetSystemConfig() - force := LuetCfg.Viper.GetBool("force") + force := viper.GetBool("force") Debug("Solver", LuetCfg.GetSolverOptions().CompactString()) diff --git a/cmd/reinstall.go b/cmd/reinstall.go index 94cfdab3..0d3c4d57 100644 --- a/cmd/reinstall.go +++ b/cmd/reinstall.go @@ -25,6 +25,7 @@ import ( pkg "github.com/mudler/luet/pkg/package" "github.com/spf13/cobra" + "github.com/spf13/viper" ) var reinstallCmd = &cobra.Command{ @@ -37,19 +38,19 @@ var reinstallCmd = &cobra.Command{ PreRun: func(cmd *cobra.Command, args []string) { util.BindSystemFlags(cmd) util.BindSolverFlags(cmd) - LuetCfg.Viper.BindPFlag("onlydeps", cmd.Flags().Lookup("onlydeps")) - LuetCfg.Viper.BindPFlag("force", cmd.Flags().Lookup("force")) - LuetCfg.Viper.BindPFlag("for", cmd.Flags().Lookup("for")) + viper.BindPFlag("onlydeps", cmd.Flags().Lookup("onlydeps")) + viper.BindPFlag("force", cmd.Flags().Lookup("force")) + viper.BindPFlag("for", cmd.Flags().Lookup("for")) - LuetCfg.Viper.BindPFlag("yes", cmd.Flags().Lookup("yes")) + viper.BindPFlag("yes", cmd.Flags().Lookup("yes")) }, Run: func(cmd *cobra.Command, args []string) { var toUninstall pkg.Packages var toAdd pkg.Packages - force := LuetCfg.Viper.GetBool("force") - onlydeps := LuetCfg.Viper.GetBool("onlydeps") - yes := LuetCfg.Viper.GetBool("yes") + force := viper.GetBool("force") + onlydeps := viper.GetBool("onlydeps") + yes := viper.GetBool("yes") downloadOnly, _ := cmd.Flags().GetBool("download-only") diff --git a/cmd/replace.go b/cmd/replace.go index c3be68e3..0a8f9336 100644 --- a/cmd/replace.go +++ b/cmd/replace.go @@ -25,6 +25,7 @@ import ( pkg "github.com/mudler/luet/pkg/package" "github.com/spf13/cobra" + "github.com/spf13/viper" ) var replaceCmd = &cobra.Command{ @@ -38,22 +39,22 @@ var replaceCmd = &cobra.Command{ PreRun: func(cmd *cobra.Command, args []string) { util.BindSystemFlags(cmd) util.BindSolverFlags(cmd) - LuetCfg.Viper.BindPFlag("onlydeps", cmd.Flags().Lookup("onlydeps")) - LuetCfg.Viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps")) - LuetCfg.Viper.BindPFlag("force", cmd.Flags().Lookup("force")) - LuetCfg.Viper.BindPFlag("for", cmd.Flags().Lookup("for")) + viper.BindPFlag("onlydeps", cmd.Flags().Lookup("onlydeps")) + viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps")) + viper.BindPFlag("force", cmd.Flags().Lookup("force")) + viper.BindPFlag("for", cmd.Flags().Lookup("for")) - LuetCfg.Viper.BindPFlag("yes", cmd.Flags().Lookup("yes")) + viper.BindPFlag("yes", cmd.Flags().Lookup("yes")) }, Run: func(cmd *cobra.Command, args []string) { var toUninstall pkg.Packages var toAdd pkg.Packages - f := LuetCfg.Viper.GetStringSlice("for") - force := LuetCfg.Viper.GetBool("force") - nodeps := LuetCfg.Viper.GetBool("nodeps") - onlydeps := LuetCfg.Viper.GetBool("onlydeps") - yes := LuetCfg.Viper.GetBool("yes") + f := viper.GetStringSlice("for") + force := viper.GetBool("force") + nodeps := viper.GetBool("nodeps") + onlydeps := viper.GetBool("onlydeps") + yes := viper.GetBool("yes") downloadOnly, _ := cmd.Flags().GetBool("download-only") util.SetSystemConfig() diff --git a/cmd/repo/update.go b/cmd/repo/update.go index 04ff4671..5a7c6d84 100644 --- a/cmd/repo/update.go +++ b/cmd/repo/update.go @@ -53,24 +53,20 @@ $> luet repo update repo1 repo2 } r := installer.NewSystemRepository(*repo) - Spinner(32) _, err = r.Sync(force) if err != nil && !ignore { Fatal("Error on sync repository " + rname + ": " + err.Error()) } - SpinnerStop() } } else { for _, repo := range LuetCfg.SystemRepositories { if repo.Cached && repo.Enable { r := installer.NewSystemRepository(repo) - Spinner(32) _, err := r.Sync(force) if err != nil && !ignore { Fatal("Error on sync repository " + r.GetName() + ": " + err.Error()) } - SpinnerStop() } } } diff --git a/cmd/root.go b/cmd/root.go index 7da98643..8137bd43 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -18,20 +18,13 @@ package cmd import ( "fmt" "os" - "os/user" - "path/filepath" - "runtime" - "strings" "github.com/marcsauter/single" + "github.com/mudler/luet/cmd/util" bus "github.com/mudler/luet/pkg/bus" - fileHelper "github.com/mudler/luet/pkg/helpers/file" - extensions "github.com/mudler/cobra-extensions" config "github.com/mudler/luet/pkg/config" - helpers "github.com/mudler/luet/pkg/helpers" . "github.com/mudler/luet/pkg/logger" - repo "github.com/mudler/luet/pkg/repository" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -63,18 +56,19 @@ func version() string { return fmt.Sprintf("%s-g%s %s", LuetCLIVersion, BuildCommit, BuildTime) } -var noBannerCommands = []string{"search", "exec", "tree", "database", "box", "cleanup"} +var bannerCommands = []string{"install", "build", "uninstall", "upgrade"} func displayVersionBanner() { - display := true + display := false if len(os.Args) > 1 { - for _, c := range noBannerCommands { + for _, c := range bannerCommands { if os.Args[1] == c { - display = false + display = true } } } if display { + util.IntroScreen() Info("Luet version", version()) Info(license) } @@ -130,10 +124,11 @@ To build a package, from a tree definition: Version: version(), PersistentPreRun: func(cmd *cobra.Command, args []string) { - err := LoadConfig(config.LuetCfg) + _, err := util.LoadConfig() if err != nil { Fatal("failed to load configuration:", err.Error()) } + // Initialize tmpdir prefix. TODO: Move this with LoadConfig // directly on sub command to ensure the creation only when it's // needed. @@ -164,41 +159,6 @@ To build a package, from a tree definition: SilenceErrors: true, } -func LoadConfig(c *config.LuetConfig) error { - // If a config file is found, read it in. - c.Viper.ReadInConfig() - - err := c.Viper.Unmarshal(&config.LuetCfg) - if err != nil { - return err - } - - noSpinner := c.Viper.GetBool("no_spinner") - - InitAurora() - if !noSpinner { - NewSpinner() - } - - Debug("Using config file:", c.Viper.ConfigFileUsed()) - - if c.GetLogging().EnableLogFile && c.GetLogging().Path != "" { - // Init zap logger - err = ZapLogger() - if err != nil { - return err - } - } - - // Load repositories - err = repo.LoadRepositories(c) - if err != nil { - return err - } - - return nil -} - // Execute adds all child commands to the root command sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { @@ -213,95 +173,5 @@ func Execute() { } func init() { - cobra.OnInitialize(initConfig) - pflags := RootCmd.PersistentFlags() - pflags.StringVar(&cfgFile, "config", "", "config file (default is $HOME/.luet.yaml)") - pflags.BoolP("debug", "d", false, "verbose output") - pflags.Bool("fatal", false, "Enables Warnings to exit") - pflags.Bool("enable-logfile", false, "Enable log to file") - pflags.Bool("no-spinner", false, "Disable spinner.") - pflags.Bool("color", config.LuetCfg.GetLogging().Color, "Enable/Disable color.") - pflags.Bool("emoji", config.LuetCfg.GetLogging().EnableEmoji, "Enable/Disable emoji.") - pflags.Bool("skip-config-protect", config.LuetCfg.ConfigProtectSkip, - "Disable config protect analysis.") - pflags.StringP("logfile", "l", config.LuetCfg.GetLogging().Path, - "Logfile path. Empty value disable log to file.") - pflags.StringSlice("plugin", []string{}, "A list of runtime plugins to load") - - // os/user doesn't work in from scratch environments. - // Check if i can retrieve user informations. - _, err := user.Current() - if err != nil { - Warning("failed to retrieve user identity:", err.Error()) - } - pflags.Bool("same-owner", config.LuetCfg.GetGeneral().SameOwner, "Maintain same owner on uncompress.") - pflags.Int("concurrency", runtime.NumCPU(), "Concurrency") - - config.LuetCfg.Viper.BindPFlag("logging.color", pflags.Lookup("color")) - config.LuetCfg.Viper.BindPFlag("logging.enable_emoji", pflags.Lookup("emoji")) - config.LuetCfg.Viper.BindPFlag("logging.enable_logfile", pflags.Lookup("enable-logfile")) - config.LuetCfg.Viper.BindPFlag("logging.path", pflags.Lookup("logfile")) - - config.LuetCfg.Viper.BindPFlag("general.concurrency", pflags.Lookup("concurrency")) - config.LuetCfg.Viper.BindPFlag("general.debug", pflags.Lookup("debug")) - config.LuetCfg.Viper.BindPFlag("general.fatal_warnings", pflags.Lookup("fatal")) - config.LuetCfg.Viper.BindPFlag("general.same_owner", pflags.Lookup("same-owner")) - config.LuetCfg.Viper.BindPFlag("plugin", pflags.Lookup("plugin")) - - // Currently I maintain this only from cli. - config.LuetCfg.Viper.BindPFlag("no_spinner", pflags.Lookup("no-spinner")) - config.LuetCfg.Viper.BindPFlag("config_protect_skip", pflags.Lookup("skip-config-protect")) - - // Extensions must be binary with the "luet-" prefix to be able to be shown in the help. - // we also accept extensions in the relative path where luet is being started, "extensions/" - exts := extensions.Discover("luet", "extensions") - for _, ex := range exts { - cobraCmd := ex.CobraCommand() - RootCmd.AddCommand(cobraCmd) - } - -} - -// initConfig reads in config file and ENV variables if set. -func initConfig() { - // Luet support these priorities on read configuration file: - // - command line option (if available) - // - $PWD/.luet.yaml - // - $HOME/.luet.yaml - // - /etc/luet/luet.yaml - // - // Note: currently a single viper instance support only one config name. - - viper.SetEnvPrefix(LuetEnvPrefix) - viper.SetConfigType("yaml") - - if cfgFile != "" { // enable ability to specify config file via flag - viper.SetConfigFile(cfgFile) - } else { - // Retrieve pwd directory - pwdDir, err := os.Getwd() - if err != nil { - Error(err) - os.Exit(1) - } - homeDir := helpers.GetHomeDir() - - if fileHelper.Exists(filepath.Join(pwdDir, ".luet.yaml")) || (homeDir != "" && fileHelper.Exists(filepath.Join(homeDir, ".luet.yaml"))) { - viper.AddConfigPath(".") - if homeDir != "" { - viper.AddConfigPath(homeDir) - } - viper.SetConfigName(".luet") - } else { - viper.SetConfigName("luet") - viper.AddConfigPath("/etc/luet") - } - } - - viper.AutomaticEnv() // read in environment variables that match - - // Create EnvKey Replacer for handle complex structure - replacer := strings.NewReplacer(".", "__") - viper.SetEnvKeyReplacer(replacer) - viper.SetTypeByDefaultValue(true) + util.InitViper(RootCmd) } diff --git a/cmd/search.go b/cmd/search.go index e0c9eb01..74e9bbdb 100644 --- a/cmd/search.go +++ b/cmd/search.go @@ -27,6 +27,7 @@ import ( . "github.com/mudler/luet/pkg/logger" pkg "github.com/mudler/luet/pkg/package" "github.com/spf13/cobra" + "github.com/spf13/viper" ) type PackageResult struct { @@ -291,7 +292,7 @@ Search can also return results in the terminal in different ways: as terminal ou PreRun: func(cmd *cobra.Command, args []string) { util.BindSystemFlags(cmd) util.BindSolverFlags(cmd) - LuetCfg.Viper.BindPFlag("installed", cmd.Flags().Lookup("installed")) + viper.BindPFlag("installed", cmd.Flags().Lookup("installed")) }, Run: func(cmd *cobra.Command, args []string) { var results Results @@ -302,7 +303,7 @@ Search can also return results in the terminal in different ways: as terminal ou } hidden, _ := cmd.Flags().GetBool("hidden") - installed := LuetCfg.Viper.GetBool("installed") + installed := viper.GetBool("installed") searchWithLabel, _ := cmd.Flags().GetBool("by-label") searchWithLabelMatch, _ := cmd.Flags().GetBool("by-label-regex") revdeps, _ := cmd.Flags().GetBool("revdeps") diff --git a/cmd/tree/validate.go b/cmd/tree/validate.go index 99033a6f..53de24c1 100644 --- a/cmd/tree/validate.go +++ b/cmd/tree/validate.go @@ -241,7 +241,7 @@ func validatePackage(p pkg.Package, checkType string, opts *ValidateOpts, recipe continue } - Spinner(32) + Spinner(22) solution, err := depSolver.Install(pkg.Packages{r}) ass := solution.SearchByName(r.GetPackageName()) SpinnerStop() diff --git a/cmd/uninstall.go b/cmd/uninstall.go index 0b755b9e..72d5492e 100644 --- a/cmd/uninstall.go +++ b/cmd/uninstall.go @@ -24,6 +24,7 @@ import ( "github.com/mudler/luet/pkg/solver" "github.com/spf13/cobra" + "github.com/spf13/viper" ) var uninstallCmd = &cobra.Command{ @@ -34,9 +35,9 @@ var uninstallCmd = &cobra.Command{ PreRun: func(cmd *cobra.Command, args []string) { util.BindSystemFlags(cmd) util.BindSolverFlags(cmd) - LuetCfg.Viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps")) - LuetCfg.Viper.BindPFlag("force", cmd.Flags().Lookup("force")) - LuetCfg.Viper.BindPFlag("yes", cmd.Flags().Lookup("yes")) + viper.BindPFlag("nodeps", cmd.Flags().Lookup("nodeps")) + viper.BindPFlag("force", cmd.Flags().Lookup("force")) + viper.BindPFlag("yes", cmd.Flags().Lookup("yes")) }, Run: func(cmd *cobra.Command, args []string) { toRemove := []pkg.Package{} @@ -49,12 +50,12 @@ var uninstallCmd = &cobra.Command{ toRemove = append(toRemove, pack) } - force := LuetCfg.Viper.GetBool("force") + force := viper.GetBool("force") nodeps, _ := cmd.Flags().GetBool("nodeps") full, _ := cmd.Flags().GetBool("full") checkconflicts, _ := cmd.Flags().GetBool("conflictscheck") fullClean, _ := cmd.Flags().GetBool("full-clean") - yes := LuetCfg.Viper.GetBool("yes") + yes := viper.GetBool("yes") keepProtected, _ := cmd.Flags().GetBool("keep-protected-files") util.SetSystemConfig() diff --git a/cmd/upgrade.go b/cmd/upgrade.go index aebfaeb6..a291cb9e 100644 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -22,6 +22,7 @@ import ( "github.com/mudler/luet/pkg/solver" "github.com/spf13/cobra" + "github.com/spf13/viper" ) var upgradeCmd = &cobra.Command{ @@ -31,19 +32,19 @@ var upgradeCmd = &cobra.Command{ PreRun: func(cmd *cobra.Command, args []string) { util.BindSystemFlags(cmd) util.BindSolverFlags(cmd) - LuetCfg.Viper.BindPFlag("force", cmd.Flags().Lookup("force")) - LuetCfg.Viper.BindPFlag("yes", cmd.Flags().Lookup("yes")) + viper.BindPFlag("force", cmd.Flags().Lookup("force")) + viper.BindPFlag("yes", cmd.Flags().Lookup("yes")) }, Long: `Upgrades packages in parallel`, Run: func(cmd *cobra.Command, args []string) { - force := LuetCfg.Viper.GetBool("force") + force := viper.GetBool("force") nodeps, _ := cmd.Flags().GetBool("nodeps") full, _ := cmd.Flags().GetBool("full") universe, _ := cmd.Flags().GetBool("universe") clean, _ := cmd.Flags().GetBool("clean") sync, _ := cmd.Flags().GetBool("sync") - yes := LuetCfg.Viper.GetBool("yes") + yes := viper.GetBool("yes") downloadOnly, _ := cmd.Flags().GetBool("download-only") util.SetSystemConfig() diff --git a/cmd/util/cli.go b/cmd/util/cli.go index 365081e0..85420b86 100644 --- a/cmd/util/cli.go +++ b/cmd/util/cli.go @@ -20,7 +20,9 @@ import ( "path/filepath" "strings" + "github.com/pterm/pterm" "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/mudler/luet/pkg/config" . "github.com/mudler/luet/pkg/config" @@ -28,30 +30,30 @@ import ( ) func BindSystemFlags(cmd *cobra.Command) { - LuetCfg.Viper.BindPFlag("system.database_path", cmd.Flags().Lookup("system-dbpath")) - LuetCfg.Viper.BindPFlag("system.rootfs", cmd.Flags().Lookup("system-target")) - LuetCfg.Viper.BindPFlag("system.database_engine", cmd.Flags().Lookup("system-engine")) + viper.BindPFlag("system.database_path", cmd.Flags().Lookup("system-dbpath")) + viper.BindPFlag("system.rootfs", cmd.Flags().Lookup("system-target")) + viper.BindPFlag("system.database_engine", cmd.Flags().Lookup("system-engine")) } func BindSolverFlags(cmd *cobra.Command) { - LuetCfg.Viper.BindPFlag("solver.type", cmd.Flags().Lookup("solver-type")) - LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount")) - LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate")) - LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts")) + viper.BindPFlag("solver.type", cmd.Flags().Lookup("solver-type")) + viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount")) + viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate")) + viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts")) } func BindValuesFlags(cmd *cobra.Command) { - LuetCfg.Viper.BindPFlag("values", cmd.Flags().Lookup("values")) + viper.BindPFlag("values", cmd.Flags().Lookup("values")) } func ValuesFlags() []string { - return LuetCfg.Viper.GetStringSlice("values") + return viper.GetStringSlice("values") } func SetSystemConfig() { - dbpath := LuetCfg.Viper.GetString("system.database_path") - rootfs := LuetCfg.Viper.GetString("system.rootfs") - engine := LuetCfg.Viper.GetString("system.database_engine") + dbpath := viper.GetString("system.database_path") + rootfs := viper.GetString("system.rootfs") + engine := viper.GetString("system.database_engine") LuetCfg.System.DatabaseEngine = engine LuetCfg.System.DatabasePath = dbpath @@ -59,10 +61,10 @@ func SetSystemConfig() { } func SetSolverConfig() (c *config.LuetSolverOptions) { - stype := LuetCfg.Viper.GetString("solver.type") - discount := LuetCfg.Viper.GetFloat64("solver.discount") - rate := LuetCfg.Viper.GetFloat64("solver.rate") - attempts := LuetCfg.Viper.GetInt("solver.max_attempts") + stype := viper.GetString("solver.type") + discount := viper.GetFloat64("solver.discount") + rate := viper.GetFloat64("solver.rate") + attempts := viper.GetInt("solver.max_attempts") LuetCfg.GetSolverOptions().Type = stype LuetCfg.GetSolverOptions().LearnRate = float32(rate) @@ -106,3 +108,14 @@ func TemplateFolders(fromRepo bool, treePaths []string) []string { } return templateFolders } + +func IntroScreen() { + luetLogo, _ := pterm.DefaultBigText.WithLetters( + pterm.NewLettersFromStringWithStyle("LU", pterm.NewStyle(pterm.FgLightMagenta)), + pterm.NewLettersFromStringWithStyle("ET", pterm.NewStyle(pterm.FgLightBlue))). + Srender() + + pterm.DefaultCenter.Print(luetLogo) + + pterm.DefaultCenter.Print(pterm.DefaultHeader.WithFullWidth().WithBackgroundStyle(pterm.NewStyle(pterm.BgLightBlue)).WithMargin(10).Sprint("Luet - 0-deps container-based package manager")) +} diff --git a/cmd/util/config.go b/cmd/util/config.go new file mode 100644 index 00000000..bc9a4a37 --- /dev/null +++ b/cmd/util/config.go @@ -0,0 +1,217 @@ +package util + +import ( + "fmt" + "os" + "os/user" + "path/filepath" + "runtime" + "strings" + + extensions "github.com/mudler/cobra-extensions" + . "github.com/mudler/luet/pkg/logger" + + "github.com/mudler/luet/pkg/config" + helpers "github.com/mudler/luet/pkg/helpers" + fileHelper "github.com/mudler/luet/pkg/helpers/file" + "github.com/mudler/luet/pkg/helpers/terminal" + repo "github.com/mudler/luet/pkg/repository" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +const ( + LuetEnvPrefix = "LUET" +) + +var cfgFile string + +// initConfig reads in config file and ENV variables if set. +func initConfig() { + // Luet support these priorities on read configuration file: + // - command line option (if available) + // - $PWD/.luet.yaml + // - $HOME/.luet.yaml + // - /etc/luet/luet.yaml + // + // Note: currently a single viper instance support only one config name. + + viper.SetEnvPrefix(LuetEnvPrefix) + viper.SetConfigType("yaml") + + if cfgFile != "" { // enable ability to specify config file via flag + viper.SetConfigFile(cfgFile) + } else { + // Retrieve pwd directory + pwdDir, err := os.Getwd() + if err != nil { + Error(err) + os.Exit(1) + } + homeDir := helpers.GetHomeDir() + + if fileHelper.Exists(filepath.Join(pwdDir, ".luet.yaml")) || (homeDir != "" && fileHelper.Exists(filepath.Join(homeDir, ".luet.yaml"))) { + viper.AddConfigPath(".") + if homeDir != "" { + viper.AddConfigPath(homeDir) + } + viper.SetConfigName(".luet") + } else { + viper.SetConfigName("luet") + viper.AddConfigPath("/etc/luet") + } + } + + viper.AutomaticEnv() // read in environment variables that match + + // Create EnvKey Replacer for handle complex structure + replacer := strings.NewReplacer(".", "__") + viper.SetEnvKeyReplacer(replacer) + viper.SetTypeByDefaultValue(true) +} + +func LoadConfig() (cc config.LuetConfig, err error) { + setDefaults(viper.GetViper()) + initConfig() + + // If a config file is found, read it in. + err = viper.ReadInConfig() + if err != nil { + return + } + + err = viper.Unmarshal(&cc) + if err != nil { + return + } + + if terminal.IsTerminal(os.Stdout) { + noSpinner := viper.GetBool("no_spinner") + InitAurora() + if !noSpinner { + NewSpinner() + } + noColor := viper.GetBool("logging.color") + if noColor { + fmt.Println("Disabling color") + NoColor() + } + } else { + fmt.Println("Not a terminal, disabling color") + NoColor() + } + + Debug("Using config file:", viper.ConfigFileUsed()) + + if cc.GetLogging().EnableLogFile && cc.GetLogging().Path != "" { + // Init zap logger + err = ZapLogger() + if err != nil { + return + } + } + + // Load repositories + err = repo.LoadRepositories(&cc) + if err != nil { + return + } + + config.LuetCfg = &cc + return +} + +func setDefaults(viper *viper.Viper) { + viper.SetDefault("logging.level", "info") + viper.SetDefault("logging.enable_logfile", false) + viper.SetDefault("logging.path", "/var/log/luet.log") + viper.SetDefault("logging.json_format", false) + viper.SetDefault("logging.enable_emoji", true) + viper.SetDefault("logging.color", true) + + viper.SetDefault("general.concurrency", runtime.NumCPU()) + viper.SetDefault("general.debug", false) + viper.SetDefault("general.show_build_output", false) + viper.SetDefault("general.spinner_ms", 100) + viper.SetDefault("general.spinner_charset", 22) + viper.SetDefault("general.fatal_warnings", false) + + u, err := user.Current() + // os/user doesn't work in from scratch environments + if err != nil || (u != nil && u.Uid == "0") { + viper.SetDefault("general.same_owner", true) + } else { + viper.SetDefault("general.same_owner", false) + } + + viper.SetDefault("system.database_engine", "boltdb") + viper.SetDefault("system.database_path", "/var/cache/luet") + viper.SetDefault("system.rootfs", "/") + viper.SetDefault("system.tmpdir_base", filepath.Join(os.TempDir(), "tmpluet")) + viper.SetDefault("system.pkgs_cache_path", "packages") + + viper.SetDefault("repos_confdir", []string{"/etc/luet/repos.conf.d"}) + viper.SetDefault("config_protect_confdir", []string{"/etc/luet/config.protect.d"}) + viper.SetDefault("config_protect_skip", false) + // TODO: Set default to false when we are ready for migration. + viper.SetDefault("config_from_host", true) + viper.SetDefault("cache_repositories", []string{}) + viper.SetDefault("system_repositories", []string{}) + viper.SetDefault("finalizer_envs", make(map[string]string, 0)) + + viper.SetDefault("solver.type", "") + viper.SetDefault("solver.rate", 0.7) + viper.SetDefault("solver.discount", 1.0) + viper.SetDefault("solver.max_attempts", 9000) +} + +func InitViper(RootCmd *cobra.Command) { + cobra.OnInitialize(initConfig) + pflags := RootCmd.PersistentFlags() + pflags.StringVar(&cfgFile, "config", "", "config file (default is $HOME/.luet.yaml)") + pflags.BoolP("debug", "d", false, "verbose output") + pflags.Bool("fatal", false, "Enables Warnings to exit") + pflags.Bool("enable-logfile", false, "Enable log to file") + pflags.Bool("no-spinner", false, "Disable spinner.") + pflags.Bool("color", config.LuetCfg.GetLogging().Color, "Enable/Disable color.") + pflags.Bool("emoji", config.LuetCfg.GetLogging().EnableEmoji, "Enable/Disable emoji.") + pflags.Bool("skip-config-protect", config.LuetCfg.ConfigProtectSkip, + "Disable config protect analysis.") + pflags.StringP("logfile", "l", config.LuetCfg.GetLogging().Path, + "Logfile path. Empty value disable log to file.") + pflags.StringSlice("plugin", []string{}, "A list of runtime plugins to load") + + // os/user doesn't work in from scratch environments. + // Check if i can retrieve user informations. + _, err := user.Current() + if err != nil { + Warning("failed to retrieve user identity:", err.Error()) + } + pflags.Bool("same-owner", config.LuetCfg.GetGeneral().SameOwner, "Maintain same owner on uncompress.") + pflags.Int("concurrency", runtime.NumCPU(), "Concurrency") + + viper.BindPFlag("logging.color", pflags.Lookup("color")) + viper.BindPFlag("logging.enable_emoji", pflags.Lookup("emoji")) + viper.BindPFlag("logging.enable_logfile", pflags.Lookup("enable-logfile")) + viper.BindPFlag("logging.path", pflags.Lookup("logfile")) + + viper.BindPFlag("general.concurrency", pflags.Lookup("concurrency")) + viper.BindPFlag("general.debug", pflags.Lookup("debug")) + viper.BindPFlag("general.fatal_warnings", pflags.Lookup("fatal")) + viper.BindPFlag("general.same_owner", pflags.Lookup("same-owner")) + viper.BindPFlag("plugin", pflags.Lookup("plugin")) + + // Currently I maintain this only from cli. + viper.BindPFlag("no_spinner", pflags.Lookup("no-spinner")) + viper.BindPFlag("config_protect_skip", pflags.Lookup("skip-config-protect")) + + // Extensions must be binary with the "luet-" prefix to be able to be shown in the help. + // we also accept extensions in the relative path where luet is being started, "extensions/" + exts := extensions.Discover("luet", "extensions") + for _, ex := range exts { + cobraCmd := ex.CobraCommand() + RootCmd.AddCommand(cobraCmd) + } + +} diff --git a/go.mod b/go.mod index 8d7250a1..aa944cab 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/apex/log v1.9.0 // indirect github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef github.com/asdine/storm v0.0.0-20190418133842-e0f77eada154 - github.com/briandowns/spinner v1.12.1-0.20201220203425-e201aaea0a31 github.com/cavaliercoder/grab v1.0.1-0.20201108051000-98a5bfe305ec github.com/containerd/cgroups v0.0.0-20200217135630-d732e370d46d // indirect github.com/containerd/containerd v1.4.1-0.20201117152358-0edc412565dc @@ -22,7 +21,6 @@ require ( github.com/docker/docker v20.10.0-beta1.0.20201110211921-af34b94a78a1+incompatible github.com/docker/go-units v0.4.0 github.com/ecooper/qlearning v0.0.0-20160612200101-3075011a69fd - github.com/fatih/color v1.12.0 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/ghodss/yaml v1.0.0 github.com/go-sql-driver/mysql v1.6.0 // indirect @@ -31,6 +29,7 @@ require ( github.com/google/go-containerregistry v0.2.1 github.com/google/renameio v1.0.0 github.com/google/uuid v1.3.0 // indirect + github.com/gookit/color v1.5.0 // indirect github.com/hashicorp/go-multierror v1.0.0 github.com/hashicorp/go-version v1.3.0 github.com/huandu/xstrings v1.3.2 // indirect @@ -44,7 +43,6 @@ require ( github.com/kyokomi/emoji v2.1.0+incompatible github.com/logrusorgru/aurora v0.0.0-20190417123914-21d75270181e github.com/marcsauter/single v0.0.0-20181104081128-f8bf46f26ec0 - github.com/mattn/go-isatty v0.0.13 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/hashstructure/v2 v2.0.1 github.com/mitchellh/mapstructure v1.4.2 // indirect @@ -62,8 +60,8 @@ require ( github.com/pelletier/go-toml v1.9.4 // indirect github.com/philopon/go-toposort v0.0.0-20170620085441-9be86dbd762f github.com/pkg/errors v0.9.1 + github.com/pterm/pterm v0.12.32-0.20211002183613-ada9ef6790c3 github.com/rancher-sandbox/gofilecache v0.0.0-20210330135715-becdeff5df15 - github.com/schollz/progressbar/v3 v3.7.1 github.com/sirupsen/logrus v1.8.1 github.com/spf13/cast v1.4.1 // indirect github.com/spf13/cobra v1.2.1 @@ -75,6 +73,8 @@ require ( golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect golang.org/x/mod v0.4.2 golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5 // indirect + golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect google.golang.org/genproto v0.0.0-20210811021853-ddbe55d93216 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index 69290ffa..a752f37c 100644 --- a/go.sum +++ b/go.sum @@ -78,6 +78,10 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go github.com/Luet-lab/moby v17.12.0-ce-rc1.0.20200605210607-749178b8f80d+incompatible h1:YddBuPhhRLoz7uhSJ3Zm//e62jQeTW/qXEZrk5I4qsk= github.com/Luet-lab/moby v17.12.0-ce-rc1.0.20200605210607-749178b8f80d+incompatible/go.mod h1:/XyFFC7lL96pE2kKmar2jd4LKxWzy1MmbiDHV0nK3bU= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= +github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= +github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= +github.com/MarvinJWendt/testza v0.2.9 h1:JTa1DprTjXFC36VmlBjHA/5NdZN8eP3cz68X60rsPAg= +github.com/MarvinJWendt/testza v0.2.9/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -142,6 +146,8 @@ github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGL github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asdine/storm v0.0.0-20190418133842-e0f77eada154 h1:2lbe+CPe6eQf2EA3jjLdLFZKGv3cbYqVIDjKnzcyOXg= github.com/asdine/storm v0.0.0-20190418133842-e0f77eada154/go.mod h1:cMLKpjHSP4q0P133fV15ojQgwWWB2IMv+hrFsmBF/wI= +github.com/atomicgo/cursor v0.0.1 h1:xdogsqa6YYlLfM+GyClC/Lchf7aiMerFiZQn7soTOoU= +github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -164,8 +170,6 @@ github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqO github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/briandowns/spinner v1.12.1-0.20201220203425-e201aaea0a31 h1:yInAg9pE5qGec5eQ7XdfOTTaGwGxD3bKFVjmD6VKkwc= -github.com/briandowns/spinner v1.12.1-0.20201220203425-e201aaea0a31/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0 h1:s7+5BfS4WFJoVF9pnB8kBk03S7pZXRdKamnV0FOl5Sc= @@ -305,8 +309,6 @@ github.com/evanphx/json-patch v0.0.0-20200808040245-162e5629780b/go.mod h1:NAJj0 github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= @@ -501,6 +503,9 @@ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsC github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.2 h1:DcFegQ7+ECdmkJMfVwWlC+89I4esJ7p8nkGt9ainGDk= github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= +github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -605,7 +610,6 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -665,21 +669,17 @@ github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHef github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= -github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-oci8 v0.0.7/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= @@ -693,8 +693,6 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N github.com/miekg/pkcs11 v1.0.2 h1:CIBkOawOtzJNE0B+EpRiUBzuVW7JEQAwdwhSS6YhIeg= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= -github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= @@ -877,10 +875,17 @@ github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= +github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= +github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= +github.com/pterm/pterm v0.12.32-0.20211002183613-ada9ef6790c3 h1:cJVCvrFnw/qdofx7T1jZETeSqJAwe/QYBLODPDwkelQ= +github.com/pterm/pterm v0.12.32-0.20211002183613-ada9ef6790c3/go.mod h1:4alQ3YuqEWAorEQ3Oz6ZqSdh6ZjIN/LEEC922dtMOhw= github.com/rancher-sandbox/gofilecache v0.0.0-20210330135715-becdeff5df15 h1:w8tg3snxZF0UHTVmYq7DDPkC3lehwFC/4EwVTAxFeWg= github.com/rancher-sandbox/gofilecache v0.0.0-20210330135715-becdeff5df15/go.mod h1:+Uhkjp4zCSryD4cpHhEu8uz4fIQ533t6Lv6M6pSVIKQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -902,8 +907,6 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/schollz/progressbar/v3 v3.7.1 h1:aQR/t6d+1nURSdoMn6c7n0vJi5xQ3KndpF0n7R5wrik= -github.com/schollz/progressbar/v3 v3.7.1/go.mod h1:CG/f0JmacksUc6TkZToO7tVq4t03zIQSQUtTd7F9GR4= github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -1014,6 +1017,8 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1087,7 +1092,6 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -1252,7 +1256,6 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1271,7 +1274,6 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201113135734-0a15ea8d9b02/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1288,11 +1290,15 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210915083310-ed5796bab164 h1:7ZDGnxgHAMw7thfC5bEos0RDAccZKxioiWBhfIe+tvw= golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/pkg/api/core/types/artifact/artifact_suite_test.go b/pkg/api/core/types/artifact/artifact_suite_test.go index 1d798e7c..39bf5c63 100644 --- a/pkg/api/core/types/artifact/artifact_suite_test.go +++ b/pkg/api/core/types/artifact/artifact_suite_test.go @@ -18,8 +18,7 @@ package artifact_test import ( "testing" - . "github.com/mudler/luet/cmd" - config "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/cmd/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -27,6 +26,6 @@ import ( func TestArtifact(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() RunSpecs(t, "Artifact Suite") } diff --git a/pkg/api/core/types/config_suite_test.go b/pkg/api/core/types/config_suite_test.go index 46663fbb..19b1a4f6 100644 --- a/pkg/api/core/types/config_suite_test.go +++ b/pkg/api/core/types/config_suite_test.go @@ -18,8 +18,7 @@ package types_test import ( "testing" - . "github.com/mudler/luet/cmd" - config "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/cmd/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -27,6 +26,6 @@ import ( func TestAPITypes(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() RunSpecs(t, "Types Suite") } diff --git a/pkg/compiler/backend/backend_suite_test.go b/pkg/compiler/backend/backend_suite_test.go index c3835de2..40367241 100644 --- a/pkg/compiler/backend/backend_suite_test.go +++ b/pkg/compiler/backend/backend_suite_test.go @@ -18,8 +18,7 @@ package backend_test import ( "testing" - . "github.com/mudler/luet/cmd" - config "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/cmd/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -27,6 +26,6 @@ import ( func TestSolver(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() RunSpecs(t, "Backend Suite") } diff --git a/pkg/compiler/backend/common.go b/pkg/compiler/backend/common.go index aadd6b21..a9327f2c 100644 --- a/pkg/compiler/backend/common.go +++ b/pkg/compiler/backend/common.go @@ -53,7 +53,7 @@ func runCommand(cmd *exec.Cmd) error { cmd.Stderr = writer if buffered { - Spinner(22) + Spinner(32) defer SpinnerStop() } diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 33f1308b..9540d92a 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -29,7 +29,6 @@ import ( "sync" "time" - "github.com/imdario/mergo" artifact "github.com/mudler/luet/pkg/api/core/types/artifact" bus "github.com/mudler/luet/pkg/bus" "github.com/mudler/luet/pkg/compiler/backend" @@ -40,6 +39,8 @@ import ( . "github.com/mudler/luet/pkg/logger" pkg "github.com/mudler/luet/pkg/package" "github.com/mudler/luet/pkg/solver" + + "github.com/imdario/mergo" "github.com/pkg/errors" "gopkg.in/yaml.v2" "helm.sh/helm/v3/pkg/chart" @@ -486,7 +487,7 @@ func (cs *LuetCompiler) waitForImages(images []string) { available, _ := oneOfImagesAvailable(images, cs.Backend) if !available { Info(fmt.Sprintf("Waiting for image %s to be available... :zzz:", images)) - Spinner(22) + Spinner(32) defer SpinnerStop() for !available { available, _ = oneOfImagesAvailable(images, cs.Backend) diff --git a/pkg/compiler/compiler_suite_test.go b/pkg/compiler/compiler_suite_test.go index 506c24d8..59978f3f 100644 --- a/pkg/compiler/compiler_suite_test.go +++ b/pkg/compiler/compiler_suite_test.go @@ -18,8 +18,7 @@ package compiler_test import ( "testing" - . "github.com/mudler/luet/cmd" - config "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/cmd/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -27,6 +26,6 @@ import ( func TestSolver(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() RunSpecs(t, "Compiler Suite") } diff --git a/pkg/compiler/types/spec/spec_suite_test.go b/pkg/compiler/types/spec/spec_suite_test.go index aba21a16..a911417f 100644 --- a/pkg/compiler/types/spec/spec_suite_test.go +++ b/pkg/compiler/types/spec/spec_suite_test.go @@ -18,8 +18,7 @@ package compilerspec_test import ( "testing" - . "github.com/mudler/luet/cmd" - config "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/cmd/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -27,6 +26,6 @@ import ( func TestSpec(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() RunSpecs(t, "Spec Suite") } diff --git a/pkg/config/config.go b/pkg/config/config.go index 02c7ed49..54c6957f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,5 +1,6 @@ // Copyright © 2019 Ettore Di Giacinto // Daniele Rondina +// 2021 Ettore Di Giacinto // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -20,9 +21,7 @@ import ( "fmt" "io/ioutil" "os" - "os/user" "path/filepath" - "runtime" "strings" "time" @@ -32,11 +31,10 @@ import ( solver "github.com/mudler/luet/pkg/solver" "github.com/pkg/errors" - v "github.com/spf13/viper" "gopkg.in/yaml.v2" ) -var LuetCfg = NewLuetConfig(v.GetViper()) +var LuetCfg = &LuetConfig{} var AvailableResolvers = strings.Join([]string{solver.QLearningResolverType}, " ") type LuetLoggingConfig struct { @@ -162,15 +160,12 @@ func (sc *LuetSystemConfig) GetRootFsAbs() (string, error) { return filepath.Abs(sc.Rootfs) } - type LuetKV struct { Key string `json:"key" yaml:"key" mapstructure:"key"` Value string `json:"value" yaml:"value" mapstructure:"value"` } type LuetConfig struct { - Viper *v.Viper `yaml:"-"` - Logging LuetLoggingConfig `yaml:"logging,omitempty" mapstructure:"logging"` General LuetGeneralConfig `yaml:"general,omitempty" mapstructure:"general"` System LuetSystemConfig `yaml:"system" mapstructure:"system"` @@ -187,59 +182,6 @@ type LuetConfig struct { ConfigProtectConfFiles []ConfigProtectConfFile `yaml:"-" mapstructure:"-"` } -func NewLuetConfig(viper *v.Viper) *LuetConfig { - if viper == nil { - viper = v.New() - } - - GenDefault(viper) - return &LuetConfig{Viper: viper, ConfigProtectConfFiles: nil} -} - -func GenDefault(viper *v.Viper) { - viper.SetDefault("logging.level", "info") - viper.SetDefault("logging.enable_logfile", false) - viper.SetDefault("logging.path", "/var/log/luet.log") - viper.SetDefault("logging.json_format", false) - viper.SetDefault("logging.enable_emoji", true) - viper.SetDefault("logging.color", true) - - viper.SetDefault("general.concurrency", runtime.NumCPU()) - viper.SetDefault("general.debug", false) - viper.SetDefault("general.show_build_output", false) - viper.SetDefault("general.spinner_ms", 100) - viper.SetDefault("general.spinner_charset", 22) - viper.SetDefault("general.fatal_warnings", false) - - u, err := user.Current() - // os/user doesn't work in from scratch environments - if err != nil || (u != nil && u.Uid == "0") { - viper.SetDefault("general.same_owner", true) - } else { - viper.SetDefault("general.same_owner", false) - } - - viper.SetDefault("system.database_engine", "boltdb") - viper.SetDefault("system.database_path", "/var/cache/luet") - viper.SetDefault("system.rootfs", "/") - viper.SetDefault("system.tmpdir_base", filepath.Join(os.TempDir(), "tmpluet")) - viper.SetDefault("system.pkgs_cache_path", "packages") - - viper.SetDefault("repos_confdir", []string{"/etc/luet/repos.conf.d"}) - viper.SetDefault("config_protect_confdir", []string{"/etc/luet/config.protect.d"}) - viper.SetDefault("config_protect_skip", false) - // TODO: Set default to false when we are ready for migration. - viper.SetDefault("config_from_host", true) - viper.SetDefault("cache_repositories", []string{}) - viper.SetDefault("system_repositories", []string{}) - viper.SetDefault("finalizer_envs", make(map[string]string, 0)) - - viper.SetDefault("solver.type", "") - viper.SetDefault("solver.rate", 0.7) - viper.SetDefault("solver.discount", 1.0) - viper.SetDefault("solver.max_attempts", 9000) -} - func (c *LuetConfig) GetSystemDB() pkg.PackageDatabase { switch LuetCfg.GetSystem().DatabaseEngine { case "boltdb": diff --git a/pkg/config/config_suite_test.go b/pkg/config/config_suite_test.go index fb67b15e..84565876 100644 --- a/pkg/config/config_suite_test.go +++ b/pkg/config/config_suite_test.go @@ -19,8 +19,7 @@ package config_test import ( "testing" - . "github.com/mudler/luet/cmd" - config "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/cmd/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -28,6 +27,6 @@ import ( func TestSolver(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() RunSpecs(t, "Config Suite") } diff --git a/pkg/helpers/helpers_suite_test.go b/pkg/helpers/helpers_suite_test.go index 1cdc29ae..83bdc96c 100644 --- a/pkg/helpers/helpers_suite_test.go +++ b/pkg/helpers/helpers_suite_test.go @@ -18,8 +18,7 @@ package helpers_test import ( "testing" - . "github.com/mudler/luet/cmd" - config "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/cmd/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -27,6 +26,6 @@ import ( func TestSolver(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() RunSpecs(t, "Helpers Suite") } diff --git a/pkg/helpers/terminal/terminal_check_bsd.go b/pkg/helpers/terminal/terminal_check_bsd.go new file mode 100644 index 00000000..d03a4f3c --- /dev/null +++ b/pkg/helpers/terminal/terminal_check_bsd.go @@ -0,0 +1,12 @@ +// +build darwin dragonfly freebsd netbsd openbsd + +package terminal + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TIOCGETA + +func isTerminal(fd int) bool { + _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + return err == nil +} diff --git a/pkg/helpers/terminal/terminal_check_general.go b/pkg/helpers/terminal/terminal_check_general.go new file mode 100644 index 00000000..8dc980e0 --- /dev/null +++ b/pkg/helpers/terminal/terminal_check_general.go @@ -0,0 +1,17 @@ +// +build !windows,!nacl,!plan9 + +package terminal + +import ( + "io" + "os" +) + +func IsTerminal(w io.Writer) bool { + switch v := w.(type) { + case *os.File: + return isTerminal(int(v.Fd())) + default: + return false + } +} diff --git a/pkg/helpers/terminal/terminal_check_no_terminal.go b/pkg/helpers/terminal/terminal_check_no_terminal.go new file mode 100644 index 00000000..f567a7ad --- /dev/null +++ b/pkg/helpers/terminal/terminal_check_no_terminal.go @@ -0,0 +1,11 @@ +// +build js nacl plan9 + +package terminal + +import ( + "io" +) + +func IsTerminal(w io.Writer) bool { + return false +} diff --git a/pkg/helpers/terminal/terminal_check_solaris.go b/pkg/helpers/terminal/terminal_check_solaris.go new file mode 100644 index 00000000..0a8e2565 --- /dev/null +++ b/pkg/helpers/terminal/terminal_check_solaris.go @@ -0,0 +1,11 @@ +package terminal + +import ( + "golang.org/x/sys/unix" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +func isTerminal(fd int) bool { + _, err := unix.IoctlGetTermio(fd, unix.TCGETA) + return err == nil +} diff --git a/pkg/helpers/terminal/terminal_check_unix.go b/pkg/helpers/terminal/terminal_check_unix.go new file mode 100644 index 00000000..8c0da7f5 --- /dev/null +++ b/pkg/helpers/terminal/terminal_check_unix.go @@ -0,0 +1,12 @@ +// +build linux aix zos + +package terminal + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TCGETS + +func isTerminal(fd int) bool { + _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + return err == nil +} diff --git a/pkg/helpers/terminal/terminal_check_windows.go b/pkg/helpers/terminal/terminal_check_windows.go new file mode 100644 index 00000000..a4f6cb8a --- /dev/null +++ b/pkg/helpers/terminal/terminal_check_windows.go @@ -0,0 +1,27 @@ +// +build windows + +package terminal + +import ( + "io" + "os" + + "golang.org/x/sys/windows" +) + +func IsTerminal(w io.Writer) bool { + switch v := w.(type) { + case *os.File: + handle := windows.Handle(v.Fd()) + var mode uint32 + if err := windows.GetConsoleMode(handle, &mode); err != nil { + return false + } + mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + if err := windows.SetConsoleMode(handle, mode); err != nil { + return false + } + return true + } + return false +} diff --git a/pkg/installer/cli.go b/pkg/installer/cli.go new file mode 100644 index 00000000..8596ce1c --- /dev/null +++ b/pkg/installer/cli.go @@ -0,0 +1,97 @@ +// Copyright © 2021 Ettore Di Giacinto +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, see . + +package installer + +import ( + "fmt" + "sort" + "strings" + + pkg "github.com/mudler/luet/pkg/package" + "github.com/pterm/pterm" +) + +func packsToList(p pkg.Packages) string { + var packs []string + + for _, pp := range p { + packs = append(packs, pp.HumanReadableString()) + } + + sort.Strings(packs) + return strings.Join(packs, " ") +} + +func printList(p pkg.Packages) { + d := pterm.TableData{{"Program Name", "Version", "License"}} + for _, m := range p { + d = append(d, []string{ + fmt.Sprintf("%s/%s", m.GetCategory(), m.GetName()), + pterm.LightGreen(m.GetVersion()), m.GetLicense()}) + } + pterm.DefaultTable.WithHasHeader().WithData(d).Render() +} + +func printUpgradeList(install, uninstall pkg.Packages) { + d := pterm.TableData{{"Old version", "New version", "License"}} + for _, m := range uninstall { + if p, err := install.Find(m.GetPackageName()); err == nil { + d = append(d, []string{ + pterm.LightRed(m.HumanReadableString()), + pterm.LightGreen(p.HumanReadableString()), m.GetLicense()}) + } else { + d = append(d, []string{ + pterm.LightRed(m.HumanReadableString()), ""}) + } + } + for _, m := range install { + if _, err := uninstall.Find(m.GetPackageName()); err != nil { + d = append(d, []string{"", + pterm.LightGreen(m.HumanReadableString()), m.GetLicense()}) + } + } + pterm.DefaultTable.WithHasHeader().WithData(d).Render() +} + +func printMatchUpgrade(artefacts map[string]ArtifactMatch, uninstall pkg.Packages) { + p := pkg.Packages{} + + for _, a := range artefacts { + p = append(p, a.Package) + } + + printUpgradeList(p, uninstall) +} + +func printMatches(artefacts map[string]ArtifactMatch) { + d := pterm.TableData{{"Program Name", "Version", "License", "Repository"}} + for _, m := range artefacts { + d = append(d, []string{ + fmt.Sprintf("%s/%s", m.Package.GetCategory(), m.Package.GetName()), + pterm.LightGreen(m.Package.GetVersion()), m.Package.GetLicense(), m.Repository.Name}) + } + pterm.DefaultTable.WithHasHeader().WithData(d).Render() +} + +func matchesToList(artefacts map[string]ArtifactMatch) string { + var packs []string + + for fingerprint, match := range artefacts { + packs = append(packs, fmt.Sprintf("%s (%s)", fingerprint, match.Repository.GetName())) + } + sort.Strings(packs) + return strings.Join(packs, " ") +} diff --git a/pkg/installer/client/client_suite_test.go b/pkg/installer/client/client_suite_test.go index d50edc00..f6d3bd79 100644 --- a/pkg/installer/client/client_suite_test.go +++ b/pkg/installer/client/client_suite_test.go @@ -18,7 +18,7 @@ package client_test import ( "testing" - . "github.com/mudler/luet/cmd" + . "github.com/mudler/luet/cmd/util" config "github.com/mudler/luet/pkg/config" . "github.com/onsi/ginkgo" @@ -27,7 +27,7 @@ import ( func TestClient(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() // Set temporary directory for rootfs config.LuetCfg.GetSystem().Rootfs = "/tmp/luet-root" // Force dynamic path for packages cache diff --git a/pkg/installer/client/http.go b/pkg/installer/client/http.go index 22f2abc5..bf39b7ef 100644 --- a/pkg/installer/client/http.go +++ b/pkg/installer/client/http.go @@ -29,16 +29,17 @@ import ( "github.com/mudler/luet/pkg/api/core/types/artifact" . "github.com/mudler/luet/pkg/logger" "github.com/pkg/errors" + "github.com/pterm/pterm" "github.com/cavaliercoder/grab" "github.com/mudler/luet/pkg/config" - - "github.com/schollz/progressbar/v3" ) type HttpClient struct { RepoData RepoData Cache *artifact.ArtifactCache + + ProgressBarArea *pterm.AreaPrinter } func NewHttpClient(r RepoData) *HttpClient { @@ -124,28 +125,11 @@ func (c *HttpClient) DownloadFile(p string) (string, error) { } resp := client.Do(req) - - bar := progressbar.NewOptions64( - resp.Size(), - progressbar.OptionSetDescription( - fmt.Sprintf("[cyan] %s - [reset]", - filepath.Base(resp.Request.HTTPRequest.URL.RequestURI()))), - progressbar.OptionSetRenderBlankState(true), - progressbar.OptionEnableColorCodes(config.LuetCfg.GetLogging().Color), - progressbar.OptionClearOnFinish(), - progressbar.OptionShowBytes(true), - progressbar.OptionShowCount(), - progressbar.OptionSetPredictTime(true), - progressbar.OptionFullWidth(), - progressbar.OptionSetTheme(progressbar.Theme{ - Saucer: "[white]=[reset]", - SaucerHead: "[white]>[reset]", - SaucerPadding: " ", - BarStart: "[", - BarEnd: "]", - })) - - bar.Reset() + pb := pterm.DefaultProgressbar.WithTotal(int(resp.Size())) + if c.ProgressBarArea != nil { + pb = pb.WithPrintTogether(c.ProgressBarArea) + } + pb, _ = pb.WithTitle(filepath.Base(resp.Request.HTTPRequest.URL.RequestURI())).Start() // start download loop t := time.NewTicker(500 * time.Millisecond) defer t.Stop() @@ -155,8 +139,9 @@ func (c *HttpClient) DownloadFile(p string) (string, error) { for { select { case <-t.C: - bar.Set64(resp.BytesComplete()) - + // bar.Set64(resp.BytesComplete()) + //pb.Increment() + pb.Increment().Current = int(resp.BytesComplete()) case <-resp.Done: // download is complete break download_loop @@ -167,11 +152,11 @@ func (c *HttpClient) DownloadFile(p string) (string, error) { continue } - Info("\nDownloaded", p, "of", + Info("Downloaded", p, "of", fmt.Sprintf("%.2f", (float64(resp.BytesComplete())/1000)/1000), "MB (", fmt.Sprintf("%.2f", (float64(resp.BytesPerSecond())/1024)/1024), "MiB/s )") - - bar.Finish() + pb.Stop() + //bar.Finish() downloaded = true break } diff --git a/pkg/installer/installer.go b/pkg/installer/installer.go index 17a0c4b6..52eae45b 100644 --- a/pkg/installer/installer.go +++ b/pkg/installer/installer.go @@ -31,11 +31,12 @@ import ( "github.com/mudler/luet/pkg/helpers" fileHelper "github.com/mudler/luet/pkg/helpers/file" "github.com/mudler/luet/pkg/helpers/match" + "github.com/mudler/luet/pkg/installer/client" . "github.com/mudler/luet/pkg/logger" pkg "github.com/mudler/luet/pkg/package" "github.com/mudler/luet/pkg/solver" + "github.com/pterm/pterm" - . "github.com/logrusorgru/aurora" "github.com/pkg/errors" ) @@ -124,27 +125,6 @@ func (l *LuetInstaller) computeUpgrade(syncedRepos Repositories, s *System) (pkg return uninstall, toInstall, nil } -func packsToList(p pkg.Packages) string { - var packs []string - - for _, pp := range p { - packs = append(packs, pp.HumanReadableString()) - } - - sort.Strings(packs) - return strings.Join(packs, " ") -} - -func matchesToList(artefacts map[string]ArtifactMatch) string { - var packs []string - - for fingerprint, match := range artefacts { - packs = append(packs, fmt.Sprintf("%s (%s)", fingerprint, match.Repository.GetName())) - } - sort.Strings(packs) - return strings.Join(packs, " ") -} - // Upgrade upgrades a System based on the Installer options. Returns error in case of failure func (l *LuetInstaller) Upgrade(s *System) error { @@ -249,13 +229,17 @@ func (l *LuetInstaller) swap(o Option, syncedRepos Repositories, toRemove pkg.Pa } if l.Options.Ask { - if len(toRemove) > 0 { - Info(":recycle: Packages that are going to be removed from the system:\n ", Yellow(packsToList(toRemove)).BgBlack().String()) - } + // if len(toRemove) > 0 { + // Info(":recycle: Packages that are going to be removed from the system:\n ", Yellow(packsToList(toRemove)).BgBlack().String()) + // } - if len(match) > 0 { - Info("Packages that are going to be installed in the system: \n ", Green(matchesToList(match)).BgBlack().String()) - } + // if len(match) > 0 { + // Info("Packages that are going to be installed in the system:") + // // Info("Packages that are going to be installed in the system: \n ", Green(matchesToList(match)).BgBlack().String()) + // printMatches(match) + // } + Info(":zap: Proposed version changes to the system:\n ") + printMatchUpgrade(match, toRemove) Info("By going forward, you are also accepting the licenses of the packages that you are going to install in your system.") if Ask() { @@ -455,24 +439,19 @@ func (l *LuetInstaller) getOpsWithOptions( } func (l *LuetInstaller) checkAndUpgrade(r Repositories, s *System) error { - Spinner(32) + // Spinner(32) uninstall, toInstall, err := l.computeUpgrade(r, s) if err != nil { return errors.Wrap(err, "failed computing upgrade") } - SpinnerStop() - - if len(uninstall) > 0 { - Info(":recycle: Packages that are going to be removed from the system:\n ", Yellow(packsToList(uninstall)).BgBlack().String()) - } - - if len(toInstall) > 0 { - Info(":zap:Packages that are going to be installed in the system:\n ", Green(packsToList(toInstall)).BgBlack().String()) - } + // SpinnerStop() if len(toInstall) == 0 && len(uninstall) == 0 { - Info("Nothing to do") + Info("Nothing to upgrade") return nil + } else { + Info(":zap: Proposed version changes to the system:\n ") + printUpgradeList(toInstall, uninstall) } // We don't want any conflict with the installed to raise during the upgrade. @@ -505,6 +484,7 @@ func (l *LuetInstaller) checkAndUpgrade(r Repositories, s *System) error { } func (l *LuetInstaller) Install(cp pkg.Packages, s *System) error { + Screen("Install") syncedRepos, err := l.SyncRepositories() if err != nil { return err @@ -560,7 +540,10 @@ func (l *LuetInstaller) Install(cp pkg.Packages, s *System) error { } } } - Info("Packages that are going to be installed in the system: \n ", Green(matchesToList(match)).BgBlack().String()) + Info("Packages that are going to be installed in the system:") + //Info("Packages that are going to be installed in the system: \n ", Green(matchesToList(match)).BgBlack().String()) + + printMatches(match) if l.Options.Ask { Info("By going forward, you are also accepting the licenses of the packages that you are going to install in your system.") @@ -581,16 +564,21 @@ func (l *LuetInstaller) download(syncedRepos Repositories, toDownload map[string var wg = new(sync.WaitGroup) + area, _ := pterm.DefaultArea.Start() + + p, _ := pterm.DefaultProgressbar.WithPrintTogether(area).WithTotal(len(toDownload)).WithTitle("Downloading packages").Start() + // Download for i := 0; i < l.Options.Concurrency; i++ { wg.Add(1) - go l.downloadWorker(i, wg, all) + go l.downloadWorker(i, wg, all, p, area) } for _, c := range toDownload { all <- c } close(all) wg.Wait() + area.Stop() return nil } @@ -782,7 +770,7 @@ func (l *LuetInstaller) checkFileconflicts(toInstall map[string]ArtifactMatch, c filesToInstall := []string{} for _, m := range toInstall { - a, err := l.downloadPackage(m) + a, err := l.downloadPackage(m, nil) if err != nil && !l.Options.Force { return errors.Wrap(err, "Failed downloading package") } @@ -877,9 +865,16 @@ func (l *LuetInstaller) install(o Option, syncedRepos Repositories, toInstall ma return s.ExecuteFinalizers(toFinalize) } -func (l *LuetInstaller) downloadPackage(a ArtifactMatch) (*artifact.PackageArtifact, error) { +func (l *LuetInstaller) downloadPackage(a ArtifactMatch, area *pterm.AreaPrinter) (*artifact.PackageArtifact, error) { - artifact, err := a.Repository.Client().DownloadArtifact(a.Artifact) + cli := a.Repository.Client() + + switch v := cli.(type) { + case *client.HttpClient: + v.ProgressBarArea = area + } + + artifact, err := cli.DownloadArtifact(a.Artifact) if err != nil { return nil, errors.Wrap(err, "Error on download artifact") } @@ -893,7 +888,7 @@ func (l *LuetInstaller) downloadPackage(a ArtifactMatch) (*artifact.PackageArtif func (l *LuetInstaller) installPackage(m ArtifactMatch, s *System) error { - a, err := l.downloadPackage(m) + a, err := l.downloadPackage(m, nil) if err != nil && !l.Options.Force { return errors.Wrap(err, "Failed downloading package") } @@ -913,18 +908,20 @@ func (l *LuetInstaller) installPackage(m ArtifactMatch, s *System) error { return s.Database.SetPackageFiles(&pkg.PackageFile{PackageFingerprint: m.Package.GetFingerPrint(), Files: files}) } -func (l *LuetInstaller) downloadWorker(i int, wg *sync.WaitGroup, c <-chan ArtifactMatch) error { +func (l *LuetInstaller) downloadWorker(i int, wg *sync.WaitGroup, c <-chan ArtifactMatch, pb *pterm.ProgressbarPrinter, area *pterm.AreaPrinter) error { defer wg.Done() for p := range c { // TODO: Keep trace of what was added from the tar, and save it into system - _, err := l.downloadPackage(p) + _, err := l.downloadPackage(p, area) if err != nil { - Fatal("Failed downloading package "+p.Package.GetName(), err.Error()) + Error("Failed downloading package "+p.Package.GetName(), err.Error()) return errors.Wrap(err, "Failed downloading package "+p.Package.GetName()) } else { - Info(":package: Package ", p.Package.HumanReadableString(), "downloaded") + Success(":package: Package ", p.Package.HumanReadableString(), "downloaded") } + pb.Increment() + } return nil @@ -1183,7 +1180,8 @@ func (l *LuetInstaller) Uninstall(s *System, packs ...pkg.Package) error { } if l.Options.Ask { - Info(":recycle: Packages that are going to be removed from the system:\n ", Yellow(packsToList(toUninstall)).BgBlack().String()) + Info(":recycle: Packages that are going to be removed from the system:") + printList(toUninstall) if Ask() { l.Options.Ask = false // Don't prompt anymore return uninstall() diff --git a/pkg/installer/installer_suite_test.go b/pkg/installer/installer_suite_test.go index 37292be4..aeaad6fd 100644 --- a/pkg/installer/installer_suite_test.go +++ b/pkg/installer/installer_suite_test.go @@ -18,7 +18,7 @@ package installer_test import ( "testing" - . "github.com/mudler/luet/cmd" + . "github.com/mudler/luet/cmd/util" config "github.com/mudler/luet/pkg/config" . "github.com/onsi/ginkgo" @@ -27,7 +27,7 @@ import ( func TestInstaller(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() // Set temporary directory for rootfs config.LuetCfg.GetSystem().Rootfs = "/tmp/luet-root" // Force dynamic path for packages cache diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 4f07644b..88c5676b 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -9,24 +9,24 @@ import ( "strings" "sync" + . "github.com/mudler/luet/pkg/config" - "github.com/briandowns/spinner" "github.com/kyokomi/emoji" . "github.com/logrusorgru/aurora" + "github.com/pterm/pterm" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) -var s *spinner.Spinner = nil +var s *pterm.SpinnerPrinter var z *zap.Logger = nil var aurora Aurora = nil var spinnerLock = sync.Mutex{} + func NewSpinner() { if s == nil { - s = spinner.New( - spinner.CharSets[LuetCfg.GetGeneral().SpinnerCharset], - LuetCfg.GetGeneral().GetSpinnerMs()) + s = pterm.DefaultSpinner.WithShowTimer(false).WithRemoveWhenDone(true) } } @@ -40,6 +40,10 @@ func GetAurora() Aurora { return aurora } +func NoColor() { + pterm.DisableColor() +} + func Ask() bool { var input string @@ -100,24 +104,41 @@ func Spinner(i int) { i = 43 } - if s != nil && !s.Active() { + if s != nil && !s.IsActive { // s.UpdateCharSet(spinner.CharSets[i]) - s.Start() // Start the spinner + //s.Start() // Start the spinner + // time.Sleep(second) + // for i := 14; i > 0; i-- { + // if i > 1 { + // introSpinner.UpdateText("Waiting for " + strconv.Itoa(i) + " seconds...") + // } else { + // introSpinner.UpdateText("Waiting for " + strconv.Itoa(i) + " second...") + // } + // time.Sleep(second) + // } + // s = introSpinner + s, _ = s.Start() + //introSpinner.Stop() } } +func Screen(text string) { + pterm.DefaultHeader.WithBackgroundStyle(pterm.NewStyle(pterm.BgLightBlue)).WithMargin(2).Println(text) + //pterm.DefaultCenter.Print(pterm.DefaultHeader.WithFullWidth().WithBackgroundStyle(pterm.NewStyle(pterm.BgLightBlue)).WithMargin(10).Sprint(text)) +} + func SpinnerText(suffix, prefix string) { if s != nil { - s.Lock() - defer s.Unlock() + spinnerLock.Lock() + defer spinnerLock.Unlock() if LuetCfg.GetGeneral().Debug { fmt.Println(fmt.Sprintf("%s %s", Bold(Cyan(prefix)).String(), Bold(Magenta(suffix)).BgBlack().String(), )) } else { - s.Suffix = Bold(Magenta(suffix)).BgBlack().String() - s.Prefix = Bold(Cyan(prefix)).String() + s.UpdateText(suffix + prefix) + } } } @@ -135,7 +156,7 @@ func SpinnerStop() { return } if s != nil { - s.Stop() + s.Success() } } @@ -147,8 +168,10 @@ func level2Number(level string) int { return 1 case "info": return 2 - default: + case "success": return 3 + default: + return 4 } } @@ -158,7 +181,7 @@ func log2File(level, msg string) { z.Error(msg) case "warning": z.Warn(msg) - case "info": + case "info", "success": z.Info(msg) default: z.Debug(msg) @@ -171,13 +194,17 @@ func level2AtomicLevel(level string) zap.AtomicLevel { return zap.NewAtomicLevelAt(zap.ErrorLevel) case "warning": return zap.NewAtomicLevelAt(zap.WarnLevel) - case "info": + case "info", "success": return zap.NewAtomicLevelAt(zap.InfoLevel) default: return zap.NewAtomicLevelAt(zap.DebugLevel) } } +func init() { + InitAurora() +} + func Msg(level string, withoutColor, ln bool, msg ...interface{}) { var message string var confLevel, msgLevel int @@ -206,7 +233,7 @@ func Msg(level string, withoutColor, ln bool, msg ...interface{}) { levelMsg = Yellow(":construction: warning" + message).BgBlack().String() case "debug": levelMsg = White(message).BgBlack().String() - case "info": + case "info", "success": levelMsg = message case "error": levelMsg = Red(message).String() @@ -225,9 +252,37 @@ func Msg(level string, withoutColor, ln bool, msg ...interface{}) { } if ln { - fmt.Println(levelMsg) + switch level { + case "info": + pterm.Info.Println(levelMsg) + case "success": + pterm.Success.Println(levelMsg) + case "warning": + pterm.Warning.Println(levelMsg) + case "error": + pterm.Error.Println(levelMsg) + case "fatal": + pterm.Fatal.Println(levelMsg) + default: + fmt.Println(levelMsg) + } + // } else { - fmt.Print(levelMsg) + switch level { + case "success": + pterm.Success.Print(levelMsg) + case "info": + pterm.Info.Print(levelMsg) + case "warning": + pterm.Warning.Print(levelMsg) + case "error": + pterm.Error.Print(levelMsg) + case "fatal": + pterm.Fatal.Print(levelMsg) + default: + fmt.Print(levelMsg) + } + } } @@ -256,6 +311,10 @@ func Info(mess ...interface{}) { Msg("info", false, true, mess...) } +func Success(mess ...interface{}) { + Msg("success", false, true, mess...) +} + func InfoC(mess ...interface{}) { Msg("info", true, true, mess...) } diff --git a/pkg/package/package_suite_test.go b/pkg/package/package_suite_test.go index 311d8ae1..c7d7130b 100644 --- a/pkg/package/package_suite_test.go +++ b/pkg/package/package_suite_test.go @@ -18,8 +18,7 @@ package pkg_test import ( "testing" - . "github.com/mudler/luet/cmd" - config "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/cmd/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -27,6 +26,6 @@ import ( func TestSolver(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() RunSpecs(t, "Package Suite") } diff --git a/pkg/repository/repository_suite_test.go b/pkg/repository/repository_suite_test.go index 3b484754..2c587a95 100644 --- a/pkg/repository/repository_suite_test.go +++ b/pkg/repository/repository_suite_test.go @@ -19,8 +19,7 @@ package repository_test import ( "testing" - . "github.com/mudler/luet/cmd" - config "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/cmd/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -28,6 +27,6 @@ import ( func TestSolver(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() RunSpecs(t, "Repository Suite") } diff --git a/pkg/repository/repository_test.go b/pkg/repository/repository_test.go index 29cd914f..9ee94d60 100644 --- a/pkg/repository/repository_test.go +++ b/pkg/repository/repository_test.go @@ -22,12 +22,11 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - viper "github.com/spf13/viper" ) var _ = Describe("Repository", func() { Context("Load Repository1", func() { - cfg := NewLuetConfig(viper.New()) + cfg := &LuetConfig{} cfg.RepositoriesConfDir = []string{ "../../tests/fixtures/repos.conf.d", } diff --git a/pkg/solver/solver_suite_test.go b/pkg/solver/solver_suite_test.go index 49c201f3..61b507f3 100644 --- a/pkg/solver/solver_suite_test.go +++ b/pkg/solver/solver_suite_test.go @@ -18,8 +18,7 @@ package solver_test import ( "testing" - . "github.com/mudler/luet/cmd" - config "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/cmd/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -27,6 +26,6 @@ import ( func TestSolver(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() RunSpecs(t, "Solver Suite") } diff --git a/pkg/spectooling/spectooling_suite_test.go b/pkg/spectooling/spectooling_suite_test.go index 778f3ad4..94f62b80 100644 --- a/pkg/spectooling/spectooling_suite_test.go +++ b/pkg/spectooling/spectooling_suite_test.go @@ -19,8 +19,7 @@ package spectooling_test import ( "testing" - . "github.com/mudler/luet/cmd" - config "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/cmd/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -28,6 +27,6 @@ import ( func TestSolver(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() RunSpecs(t, "Spec Tooling Suite") } diff --git a/pkg/tree/tree_suite_test.go b/pkg/tree/tree_suite_test.go index 89bb04f5..b602e4aa 100644 --- a/pkg/tree/tree_suite_test.go +++ b/pkg/tree/tree_suite_test.go @@ -18,8 +18,7 @@ package tree_test import ( "testing" - . "github.com/mudler/luet/cmd" - config "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/cmd/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -27,6 +26,6 @@ import ( func TestTree(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() RunSpecs(t, "Tree Suite") } diff --git a/pkg/versioner/versioner_suite_test.go b/pkg/versioner/versioner_suite_test.go index 55fec9c0..c68e1b76 100644 --- a/pkg/versioner/versioner_suite_test.go +++ b/pkg/versioner/versioner_suite_test.go @@ -18,8 +18,7 @@ package version_test import ( "testing" - . "github.com/mudler/luet/cmd" - config "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/cmd/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -27,6 +26,6 @@ import ( func TestVersioner(t *testing.T) { RegisterFailHandler(Fail) - LoadConfig(config.LuetCfg) + LoadConfig() RunSpecs(t, "Versioner Suite") }