From f576eec3c33d4c39b24e404cdc9d5e7c14e705d0 Mon Sep 17 00:00:00 2001 From: DataHearth Date: Sun, 10 Jul 2022 12:35:01 +0200 Subject: [PATCH] feat(pkgs): add more package manager --- .config-mapper.yml.template | 8 +- README.md | 13 +- cmd/cli.go | 12 +- internal/configuration/definition.go | 8 +- internal/pkgs.go | 194 ++++++++------------------- 5 files changed, 77 insertions(+), 158 deletions(-) diff --git a/.config-mapper.yml.template b/.config-mapper.yml.template index 2e56fbb..b7c44a8 100644 --- a/.config-mapper.yml.template +++ b/.config-mapper.yml.template @@ -10,7 +10,7 @@ storage: basic-auth: username: USERNAME # * NOTE: if you're having trouble with error "authentication required", you should maybe use a token access - # * In some cases, it's due to 2FA authentication enabled on the git hosting provided + # * In some cases, it's due to 2FA authentication enabled on the git hosting provider password: TOKEN # * by default, if ssh dict is set with its keys filled, I'll try to clone with SSH ssh: @@ -19,7 +19,7 @@ storage: passphrase: PASSPHRASE # NOTE: the $LOCATION if refering to the "storage.location" path. It'll be replaced automatically -# The left part of ":" is your repository location and right part when it should be on your system +# The left part of ":" is your repository location and right part where it should be located on your system files: - darwin: "$LOCATION/macos/.zshrc:~/.zshrc" linux: "$LOCATION/linux/.zshrc:~/.zshrc" @@ -29,7 +29,7 @@ folders: linux: "$LOCATION/macos/.config:~/.config" package-managers: - installation-order: ["homebrew"] + installation-order: ["homebrew"] # available: brew, pip (pip check also for pip3), cargo, apt, npm, go homebrew: - bat - hexyl @@ -46,5 +46,3 @@ package-managers: - nmap - pinentry - zsh - - apt-get: [] diff --git a/README.md b/README.md index 59b318d..c3d348a 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ config-mapper save All defined files and folders will be copied inside your repository. -If you want to exclude one part of your configuration file (files, folders), you can use these flags to ignore them `--disable-files` `--disable-folders`. Note, package managers are disable by default. You can enable this option using the `--pkgs` flag. +If you want to exclude one part of your configuration file (files, folders), you can use these flags to ignore them `--disable-files` `--disable-folders`. You can also exclude files and folders from a given directory with a `.gitignore` like file named `.ignore`. Put it in the root directory of an included folder and add relative path to exclude (does not support glob for now). E.g: @@ -106,8 +106,6 @@ foo/bar egg ``` -If `homebrew` is provided in the `installation-order` (default: `["apt", "homebrew"]`), it will override the `homebrew` field with all user installed packages (`brew leaves --installed-on-request`). The same principle will be implemented with `Advanced Package Tool`. - template for your configuration: ```yaml @@ -122,8 +120,9 @@ folders: linux: "$LOCATION/macos/.config:~/.config" package-managers: - installation-order: ["homebrew"] - homebrew: + installation-order: + - brew + brew: - bat - hexyl - fd @@ -141,6 +140,10 @@ package-managers: - zsh apt-get: [] + pip: [] + cargo: [] + pip: [] + go: [] ``` ### Load your configuration onto the system diff --git a/cmd/cli.go b/cmd/cli.go index 20017f5..7cb3dce 100644 --- a/cmd/cli.go +++ b/cmd/cli.go @@ -54,6 +54,7 @@ func init() { rootCmd.AddCommand(initCmd) rootCmd.AddCommand(loadCmd) rootCmd.AddCommand(saveCmd) + rootCmd.PersistentFlags().BoolP("verbose", "v", false, "STDOUT will be more verbose") rootCmd.PersistentFlags().StringP("configuration-file", "c", "", "location of configuration file") rootCmd.PersistentFlags().String("ssh-user", "", "SSH username to retrieve configuration file") @@ -74,13 +75,11 @@ func init() { saveCmd.Flags().Bool("disable-files", false, "files will be ignored") saveCmd.Flags().Bool("disable-folders", false, "folders will be ignored") - saveCmd.Flags().Bool("pkgs", false, "packages will be saved") saveCmd.Flags().BoolP("push", "p", false, "new configurations will be committed and pushed") saveCmd.Flags().StringP("message", "m", strconv.FormatInt(time.Now().Unix(), 10), "combined with --push to set a commit message") saveCmd.Flags().Bool("disable-index", false, "configuration index will not be updated") viper.BindPFlag("save-disable-files", saveCmd.Flags().Lookup("disable-files")) viper.BindPFlag("save-disable-folders", saveCmd.Flags().Lookup("disable-folders")) - viper.BindPFlag("save-enable-pkgs", saveCmd.Flags().Lookup("pkgs")) viper.BindPFlag("push", saveCmd.Flags().Lookup("push")) viper.BindPFlag("disable-index-update", saveCmd.Flags().Lookup("disable-index")) viper.BindPFlag("message", saveCmd.Flags().Lookup("message")) @@ -123,13 +122,6 @@ func save(cmd *cobra.Command, args []string) { el.Action("save") - if viper.GetBool("save-enable-pkgs") { - if err := mapper.SavePkgs(c); err != nil { - mapper.PrintError(err.Error()) - os.Exit(1) - } - } - if err := el.CleanUp(indexer.RemovedLines()); err != nil { mapper.PrintError("failed to clean repository: %v\n", err) os.Exit(1) @@ -178,7 +170,7 @@ func load(cmd *cobra.Command, args []string) { el.Action("load") if viper.GetBool("load-enable-pkgs") { - if err := mapper.LoadPkgs(c.PackageManagers); err != nil { + if err := mapper.InstallPackages(c.PackageManagers); err != nil { mapper.PrintError(err.Error()) os.Exit(1) } diff --git a/internal/configuration/definition.go b/internal/configuration/definition.go index 36690d4..f0ac2d9 100644 --- a/internal/configuration/definition.go +++ b/internal/configuration/definition.go @@ -37,6 +37,10 @@ type Ssh struct { type PkgManagers struct { InstallationOrder []string `mapstructure:"installation-order" yaml:"installation-order"` - Homebrew []string `mapstructure:"homebrew" yaml:"homebrew"` - Aptitude []string `mapstructure:"apt-get" yaml:"apt-get"` + Brew []string `mapstructure:"brew" yaml:"brew"` + Apt []string `mapstructure:"apt" yaml:"apt"` + Cargo []string `mapstructure:"cargo" yaml:"cargo"` + Pip []string `mapstructure:"pip" yaml:"pip"` + Npm []string `mapstructure:"npm" yaml:"npm"` + Go []string `mapstructure:"go" yaml:"go"` } diff --git a/internal/pkgs.go b/internal/pkgs.go index 4afb725..4c72eb9 100644 --- a/internal/pkgs.go +++ b/internal/pkgs.go @@ -1,161 +1,83 @@ package mapper import ( - "errors" "fmt" "os" "os/exec" - "strings" "github.com/datahearth/config-mapper/internal/configuration" "github.com/fatih/color" "github.com/gernest/wow" "github.com/gernest/wow/spin" "github.com/spf13/viper" - "gopkg.in/yaml.v3" ) -var ( - ErrFailedInstallation = errors.New("failed to install some packages. Please, checkout STDERR for more information") - ErrFailedSaving = errors.New("failed to save some packages. Please, checkout STDERR for more information") - ErrBrewNotAvailable = errors.New("homebrew is not available on your system") - ErrAptNotAvailable = errors.New("aptitude is not available on your system") -) - -// LoadPkgs triggers related functions with passed order -func LoadPkgs(c configuration.PkgManagers) error { +// InstallPackages install all packages from the configuration file by installation order +func InstallPackages(c configuration.PkgManagers) error { color.Blue("\n# Installing packages") - for _, pkg := range c.InstallationOrder { - switch pkg { - case "homebrew": - if err := installBrewPkgs(c.Homebrew); err != nil { - PrintError(err.Error()) - return ErrFailedInstallation - } + for _, pkgManager := range c.InstallationOrder { + var pkgs []string + switch pkgManager { + case "brew": + pkgs = c.Brew case "apt": - if err := installAptPkgs(c.Aptitude); err != nil { - PrintError(err.Error()) - return ErrFailedInstallation + pkgs = c.Apt + case "cargo": + pkgs = c.Cargo + case "npm": + pkgs = c.Npm + case "pip": + pkgs = c.Pip + case "go": + pkgs = c.Go + default: + PrintError("package manager not supported: %s", pkgManager) + continue + } + + if _, err := exec.LookPath(pkgManager); err != nil { + if pkgManager == "pip" { + if _, err := exec.LookPath("pip3"); err != nil { + return fmt.Errorf("%s and pip3 are not available on your system", pkgManager) + } + pkgManager = "pip3" + } else { + return fmt.Errorf("%s is not available on your system", pkgManager) } } - } - return nil -} - -// SavePkgs triggers related functions with passed order -func SavePkgs(cfg configuration.Configuration) error { - color.Blue("# Saving user installed packages") - - for _, pkg := range cfg.PackageManagers.InstallationOrder { - switch pkg { - case "homebrew": - if err := saveBrewPkgs(cfg); err != nil { - PrintError(err.Error()) - return ErrFailedSaving - } - case "apt": - fmt.Println("implemented soon!") + if len(pkgs) == 0 { + fmt.Printf("%s: nothing to do\n", pkgManager) + return nil } + + cmd := exec.Command(pkgManager, "install") + cmd.Args = append(cmd.Args, pkgs...) + color.Blue("\n## Installing %s packages", pkgManager) + + spinner := wow.New(os.Stdout, spin.Get(spin.Dots3), " Running...") + + v := viper.GetBool("verbose") + if v { + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + } else { + spinner.Start() + } + + if err := cmd.Run(); err != nil { + spinner.Stop() + PrintError("\n%s command failed: %v", pkgManager, err) + return err + } + + if v { + // todo: find a way to clear spinner when done + spinner.Stop() + } + color.Green("\n%s Packages intalled succesfully !", pkgManager) } return nil } - -// saveBrewPkgs gather user installed packages by running `brew leaves --installed-on-request`. -// It captures the output, parse it and save it into the configuration. -func saveBrewPkgs(cfg configuration.Configuration) error { - if _, err := exec.LookPath("brew"); err != nil { - return err - } - - color.Blue("\n## Saving Homebrew packages") - - o, err := exec.Command("brew", "leaves", "--installed-on-request").Output() - if err != nil { - return err - } - - pkgs := strings.Split(string(o), "\n") - cfg.PackageManagers.Homebrew = append([]string{}, pkgs[:len(pkgs)-1]...) - - b, err := yaml.Marshal(cfg) - if err != nil { - return err - } - - if err := os.WriteFile(viper.GetString("configuration-file"), b, 0755); err != nil { - return err - } - - color.Green("Packages saved succesfully !") - return nil -} - -// installBrewPkgs installs homebrew packages by passing them to homebrew's CLI. -// STDERR and STDOUT are captured if verbose flag is passed. -func installBrewPkgs(pkgs []string) error { - if _, err := exec.LookPath("brew"); err != nil { - return ErrBrewNotAvailable - } - - if len(pkgs) == 0 { - fmt.Println("homebrew: nothing to do") - return nil - } - - cmd := exec.Command("brew", "install") - cmd.Args = append(cmd.Args, pkgs...) - color.Blue("\n## Installing Homebrew packages") - - spinner := wow.New(os.Stdout, spin.Get(spin.Dots3), " Running...") - - v := viper.GetBool("verbose") - if v { - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - } else { - spinner.Start() - } - - if err := cmd.Run(); err != nil { - spinner.Stop() - PrintError("brew command failed: %v", err) - return err - } - - if v { - // todo: find a way to clear spinner when done - spinner.Stop() - } - color.Green("\nPackages intalled succesfully !") - - return nil -} - -// installAptPkgs installs all provided "apt" packages by passing them to the Advanced Package Tool's CLI -func installAptPkgs(pkgs []string) error { - if _, err := exec.LookPath("apt"); err != nil { - return ErrAptNotAvailable - } - - if len(pkgs) == 0 { - fmt.Println("apt: nothing to do") - return nil - } - - cmd := exec.Command("sudo", "apt", "install") - cmd.Args = append(cmd.Args, pkgs...) - - color.Blue("\n## Installing apt packages") - - if err := cmd.Run(); err != nil { - PrintError("apt command failed: %v", err) - return err - } - - color.Green("Packages intalled succesfully !") - - return nil -}