From 5b7775f33e999514651935ad9dc5083c89119168 Mon Sep 17 00:00:00 2001 From: Patryk Hegenberg Date: Sat, 18 Jan 2025 14:34:16 +0100 Subject: [PATCH] refactor: seperate files, function and structs into seperate packages --- .goreleaser.yaml | 3 + cmd.go => cmd/cmd.go | 13 +- install_cmd.go => cmd/install_cmd.go | 23 ++- .../package_commands.go | 2 +- remove_cmd.go => cmd/remove_cmd.go | 23 ++- search_cmd.go => cmd/search_cmd.go | 20 +-- main.go => cmd/sst/main.go | 3 +- config.toml | 99 ++++++++++- config.go => config/config.go | 11 +- dotfiles.go | 42 ----- dotfiles/dotfiles.go | 43 +++++ go.mod | 7 +- go.sum | 2 - internal/shell/shell.go | 23 +++ magefile.go | 1 - model.go | 164 ------------------ cargo.go => packagemanager/cargo.go | 16 +- cargo_test.go => packagemanager/cargo_test.go | 11 +- flatpak.go => packagemanager/flatpak.go | 30 +++- .../flatpak_test.go | 8 +- golang.go => packagemanager/golang.go | 16 +- .../golang_test.go | 12 +- homebrew.go => packagemanager/homebrew.go | 15 +- .../homebrew_test.go | 12 +- osinfo.go => packagemanager/os.go | 52 +++--- osinfo_test.go => packagemanager/os_test.go | 23 +-- package.go => packagemanager/package.go | 19 +- .../packagemanager.go | 2 +- pipx.go => packagemanager/pipx.go | 16 +- pipx_test.go => packagemanager/pipx_test.go | 12 +- packagemanager/utils.go | 19 ++ tui.go | 146 ---------------- tui/tui.go | 115 ++++++++++++ utils.go | 64 ------- utils/utils.go | 32 ++++ utils/utils_test.go | 40 +++++ utils_test.go | 85 --------- 37 files changed, 547 insertions(+), 677 deletions(-) rename cmd.go => cmd/cmd.go (87%) rename install_cmd.go => cmd/install_cmd.go (65%) rename package_commands.go => cmd/package_commands.go (99%) rename remove_cmd.go => cmd/remove_cmd.go (63%) rename search_cmd.go => cmd/search_cmd.go (77%) rename main.go => cmd/sst/main.go (55%) rename config.go => config/config.go (59%) delete mode 100644 dotfiles.go create mode 100644 dotfiles/dotfiles.go create mode 100644 internal/shell/shell.go delete mode 100644 model.go rename cargo.go => packagemanager/cargo.go (60%) rename cargo_test.go => packagemanager/cargo_test.go (72%) rename flatpak.go => packagemanager/flatpak.go (71%) rename flatpak_test.go => packagemanager/flatpak_test.go (83%) rename golang.go => packagemanager/golang.go (60%) rename golang_test.go => packagemanager/golang_test.go (72%) rename homebrew.go => packagemanager/homebrew.go (68%) rename homebrew_test.go => packagemanager/homebrew_test.go (73%) rename osinfo.go => packagemanager/os.go (81%) rename osinfo_test.go => packagemanager/os_test.go (63%) rename package.go => packagemanager/package.go (62%) rename packagemanager.go => packagemanager/packagemanager.go (90%) rename pipx.go => packagemanager/pipx.go (60%) rename pipx_test.go => packagemanager/pipx_test.go (71%) create mode 100644 packagemanager/utils.go delete mode 100644 tui.go create mode 100644 tui/tui.go delete mode 100644 utils.go create mode 100644 utils/utils.go create mode 100644 utils/utils_test.go delete mode 100644 utils_test.go diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 3a2d8da..2f22d5a 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -3,6 +3,9 @@ version: 2 +env: + - GITHUB_TOKEN={{ .Env.CODEBERG_TOKEN }} + before: hooks: - go mod tidy diff --git a/cmd.go b/cmd/cmd.go similarity index 87% rename from cmd.go rename to cmd/cmd.go index 598ccdb..102668c 100644 --- a/cmd.go +++ b/cmd/cmd.go @@ -1,23 +1,24 @@ -package main +package cmd import ( "fmt" "os" + "system_setup_tool/tui" "github.com/spf13/cobra" "github.com/spf13/viper" ) -var rootCmd = &cobra.Command{ +var RootCmd = &cobra.Command{ Use: "system_setup_tool", Short: "Installs packages based on TOML configuration", - Run: run, + Run: tui.Run, } func init() { cobra.OnInitialize(initConfig) - rootCmd.PersistentFlags().StringP("config", "c", "", "Path to the configuration file") - viper.BindPFlag("config", rootCmd.PersistentFlags().Lookup("config")) + RootCmd.PersistentFlags().StringP("config", "c", "", "Path to the configuration file") + viper.BindPFlag("config", RootCmd.PersistentFlags().Lookup("config")) addCmd.Flags().StringP("name", "n", "", "The name of the package you want to add") addCmd.Flags().StringP("manager", "m", "", "The package manager you want to add the package to (homebrew|cargo|flatpak|pipx|go)") @@ -38,7 +39,7 @@ func init() { searchCmd.Flags().StringP("manager", "m", "", "The package manager you want to search a package with. (Options: os|homebrew|flatpak)") packageCmd.AddCommand(addCmd, deleteCmd, showCmd, enableCmd) - rootCmd.AddCommand(packageCmd, searchCmd, installCmd, removeCmd) + RootCmd.AddCommand(packageCmd, searchCmd, installCmd, removeCmd) } func initConfig() { diff --git a/install_cmd.go b/cmd/install_cmd.go similarity index 65% rename from install_cmd.go rename to cmd/install_cmd.go index 468ab9f..ed36c43 100644 --- a/install_cmd.go +++ b/cmd/install_cmd.go @@ -1,8 +1,11 @@ -package main +package cmd import ( "fmt" "log" + "system_setup_tool/utils" + + pm "system_setup_tool/packagemanager" "github.com/spf13/cobra" ) @@ -14,29 +17,25 @@ var installCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { packageName := args[0] managerName, _ := cmd.Flags().GetString("manager") - var manager PackageManager + var manager pm.PackageManager switch managerName { case "os": - opSys, err := getLinuxDistribution() - if err != nil { - log.Printf("error getting OS information: %v", err) - } - sudoPassword, err := getSudoPassword() + sudoPassword, err := utils.GetSudoPassword() if err != nil { log.Fatal(err) } - osManager := NewOSManager(opSys, sudoPassword, []string{packageName}) + osManager := pm.NewOSManager(sudoPassword) if err := osManager.Install([]string{packageName}); err != nil { log.Printf("error: %v\n", err) } case "homebrew": - manager = &HomebrewManager{} + manager = &pm.HomebrewManager{} case "cargo": - manager = &CargoManager{} + manager = &pm.CargoManager{} case "pipx": - manager = &PipxManager{} + manager = &pm.PipxManager{} case "flatpak": - manager = &FlatpakManager{} + manager = &pm.FlatpakManager{} default: fmt.Println("No PackageManager found") } diff --git a/package_commands.go b/cmd/package_commands.go similarity index 99% rename from package_commands.go rename to cmd/package_commands.go index a73126d..d3b50f5 100644 --- a/package_commands.go +++ b/cmd/package_commands.go @@ -1,4 +1,4 @@ -package main +package cmd import ( "fmt" diff --git a/remove_cmd.go b/cmd/remove_cmd.go similarity index 63% rename from remove_cmd.go rename to cmd/remove_cmd.go index 6609578..7e36cc6 100644 --- a/remove_cmd.go +++ b/cmd/remove_cmd.go @@ -1,7 +1,10 @@ -package main +package cmd import ( "log" + "system_setup_tool/utils" + + pm "system_setup_tool/packagemanager" "github.com/spf13/cobra" ) @@ -13,29 +16,25 @@ var removeCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { packageName := args[0] managerName, _ := cmd.Flags().GetString("manager") - var manager PackageManager + var manager pm.PackageManager switch managerName { case "os": - opSys, err := getLinuxDistribution() - if err != nil { - log.Printf("error getting OS information: %v", err) - } - sudoPassword, err := getSudoPassword() + sudoPassword, err := utils.GetSudoPassword() if err != nil { log.Fatal(err) } - osManager := NewOSManager(opSys, sudoPassword, []string{packageName}) + osManager := pm.NewOSManager(sudoPassword) if err := osManager.Install([]string{packageName}); err != nil { log.Printf("error: %v\n", err) } case "homebrew": - manager = &HomebrewManager{} + manager = &pm.HomebrewManager{} case "cargo": - manager = &CargoManager{} + manager = &pm.CargoManager{} case "pipx": - manager = &PipxManager{} + manager = &pm.PipxManager{} case "flatpak": - manager = &FlatpakManager{} + manager = &pm.FlatpakManager{} default: } if err := manager.RemovePackage(packageName); err != nil { diff --git a/search_cmd.go b/cmd/search_cmd.go similarity index 77% rename from search_cmd.go rename to cmd/search_cmd.go index 8514355..1bc77bc 100644 --- a/search_cmd.go +++ b/cmd/search_cmd.go @@ -1,10 +1,11 @@ -package main +package cmd import ( "fmt" - "log" "strings" + pm "system_setup_tool/packagemanager" + "github.com/spf13/cobra" ) @@ -18,16 +19,11 @@ var searchCmd = &cobra.Command{ if managerName == "os" { managerName = "OS Package Manager" } - opSys, err := getLinuxDistribution() - if err != nil { - log.Printf("error getting OS information: %v", err) - return - } - managers := []PackageManager{ - NewOSManager(opSys, "", nil), - &HomebrewManager{}, - &FlatpakManager{}, + managers := []pm.PackageManager{ + pm.NewOSManager(""), + &pm.HomebrewManager{}, + &pm.FlatpakManager{}, } if managerName != "" { @@ -57,7 +53,7 @@ func displayResults(results []string, manager string) { } } -func searchAndDisplayResults(manager PackageManager, packageName string) { +func searchAndDisplayResults(manager pm.PackageManager, packageName string) { fmt.Printf("Searching in %s:\n", manager.Name()) results := manager.SearchPackage(packageName) displayResults(results, manager.Name()) diff --git a/main.go b/cmd/sst/main.go similarity index 55% rename from main.go rename to cmd/sst/main.go index 376027d..959f384 100644 --- a/main.go +++ b/cmd/sst/main.go @@ -3,10 +3,11 @@ package main import ( "log" "os" + "system_setup_tool/cmd" ) func main() { - if err := rootCmd.Execute(); err != nil { + if err := cmd.RootCmd.Execute(); err != nil { log.Println(err) os.Exit(1) } diff --git a/config.toml b/config.toml index e3f63a0..9d94c2c 100644 --- a/config.toml +++ b/config.toml @@ -12,20 +12,107 @@ packages = ['typst-cli'] [package_managers.flatpak] enable = true -packages = ['com.spotify.Client', 'us.zoom.Zoom', 'org.zotero.Zotero', 'com.google.AndroidStudio', 'io.freetubeapp.FreeTube', 'com.discordapp.Discord', 'com.nextcloud.desktopclient.nextcloud', 'com.github.tchx84.Flatseal', 'io.github.flattool.Warehouse', 'org.onlyoffice.desktopeditors'] +packages = [ + 'com.spotify.Client', + 'us.zoom.Zoom', + 'org.zotero.Zotero', + 'com.google.AndroidStudio', + 'io.freetubeapp.FreeTube', + 'com.discordapp.Discord', + 'com.nextcloud.desktopclient.nextcloud', + 'com.github.tchx84.Flatseal', + 'io.github.flattool.Warehouse', + 'org.onlyoffice.desktopeditors', +] [package_managers.go] enable = true -packages = ['github.com/stefanlogue/meteor', 'golang.org/x/tools/gopls', 'github.com/go-delve/delve/cmd/dlv', 'github.com/air-verse/air'] +packages = [ + 'github.com/stefanlogue/meteor', + 'golang.org/x/tools/gopls', + 'github.com/go-delve/delve/cmd/dlv', + 'github.com/air-verse/air', +] [package_managers.homebrew] enable = true -packages = ['fd', 'fzf', 'ripgrep', 'neovim', 'helix', 'node', 'yazi', 'zk', 'bat', 'bottom', 'btop', 'git-cliff', 'glow', 'lazygit', 'goreleaser', 'harlequin', 'mage', 'posting', 'typst', 'wails', 'zoxide', 'lsd', 'jq', 'yq', 'timewarrior', 'tmux', 'fastfetch', 'stow', 'distrobox'] +packages = [ + 'oh-my-posh', + 'fd', + 'fzf', + 'ripgrep', + 'neovim', + 'helix', + 'node', + 'yazi', + 'zk', + 'bat', + 'bottom', + 'btop', + 'git-cliff', + 'glow', + 'lazygit', + 'goreleaser', + 'harlequin', + 'mage', + 'posting', + 'typst', + 'wails', + 'zoxide', + 'lsd', + 'jq', + 'yq', + 'timewarrior', + 'tmux', + 'fastfetch', + 'stow', + 'distrobox', +] [package_managers.pipx] enable = true packages = ['euporie'] -[packages] -headless = ['git', 'curl', 'wget', 'biber', 'bear', 'docker', 'docker-compose', 'zsh', 'npm', 'task', 'tree-sitter-cli', 'python3-pip', 'latexmk', 'luarocks'] -non_headless = ['flameshot', 'fuzzel', 'hyprcursor', 'hypridle', 'hyprland', 'hyprland-qtutils', 'hyprlock', 'hyprpaper', 'hyprutils', 'kitty', 'mako', 'SwayNotificationCenter', 'SwayNotificationCenter-zsh-completion', 'mpv', 'pidgin', 'remmina', 'thunderbird-i18n-de', 'virt-manager', 'vlc', 'waybar', 'nwg-panel', 'nwg-look', 'xdg-desktop-portal-hyprland', 'zathura', 'zathura-pdf-mupdf'] +[package_managers.os] +enable = true +packages = [ + 'git', + 'curl', + 'wget', + 'biber', + 'bear', + 'docker', + 'docker-compose', + 'zsh', + 'npm', + 'task', + 'tree-sitter-cli', + 'python3-pip', + 'latexmk', + 'luarocks', + 'flameshot', + 'fuzzel', + 'hyprcursor', + 'hypridle', + 'hyprland', + 'hyprland-qtutils', + 'hyprlock', + 'hyprpaper', + 'hyprutils', + 'kitty', + 'mako', + 'SwayNotificationCenter', + 'SwayNotificationCenter-zsh-completion', + 'mpv', + 'pidgin', + 'remmina', + 'thunderbird-i18n-de', + 'virt-manager', + 'vlc', + 'waybar', + 'nwg-panel', + 'nwg-look', + 'xdg-desktop-portal-hyprland', + 'zathura', + 'zathura-pdf-mupdf', +] diff --git a/config.go b/config/config.go similarity index 59% rename from config.go rename to config/config.go index 287a3fa..fddb47a 100644 --- a/config.go +++ b/config/config.go @@ -1,4 +1,9 @@ -package main +package config + +import ( + "system_setup_tool/dotfiles" + pm "system_setup_tool/packagemanager" +) type PackageManagerConfig struct { Enable bool `mapstructure:"enable"` @@ -7,7 +12,7 @@ type PackageManagerConfig struct { type Config struct { Headless bool `mapstructure:"headless"` - Packages Packages `mapstructure:"packages"` + Packages pm.Packages `mapstructure:"packages"` PackageManagers map[string]PackageManagerConfig `mapstructure:"package_managers"` - Dotfiles DotfilesConfig `mapstructure:"dotfiles"` + Dotfiles dotfiles.DotfilesConfig `mapstructure:"dotfiles"` } diff --git a/dotfiles.go b/dotfiles.go deleted file mode 100644 index 849da76..0000000 --- a/dotfiles.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "fmt" - "log" - "os" - "path/filepath" -) - -type DotfilesConfig struct { - Enable bool `mapstructure:"enable"` - GitRepo string `mapstructure:"git_repo"` -} - -func setupDotfiles(config DotfilesConfig) error { - if _, err := execLookPath("git"); err != nil { - return fmt.Errorf("git ist nicht installiert") - } - - if _, err := execLookPath("stow"); err != nil { - return fmt.Errorf("gnu stow ist nicht installiert") - } - - dotfilesDir := filepath.Join(os.Getenv("HOME"), "dotfiles") - - cmd := execCommand("git", "clone", config.GitRepo, dotfilesDir) - if err := cmd.Run(); err != nil { - return fmt.Errorf("fehler beim Klonen der Dotfiles: %v", err) - } - - if err := os.Chdir(dotfilesDir); err != nil { - return fmt.Errorf("fehler beim Wechseln in das Dotfiles-Verzeichnis: %v", err) - } - - cmd = execCommand("stow", ".", "--override='*'") - if err := cmd.Run(); err != nil { - log.Printf("Fehler beim Linken: %v", err) - } - fmt.Printf("Alles erfolgreich verlinkt\n") - - return nil -} diff --git a/dotfiles/dotfiles.go b/dotfiles/dotfiles.go new file mode 100644 index 0000000..f6eea51 --- /dev/null +++ b/dotfiles/dotfiles.go @@ -0,0 +1,43 @@ +package dotfiles + +import ( + "fmt" + "log" + "os" + "path/filepath" + "system_setup_tool/internal/shell" +) + +type DotfilesConfig struct { + Enable bool `mapstructure:"enable"` + GitRepo string `mapstructure:"git_repo"` +} + +func SetupDotfiles(config DotfilesConfig) error { + if _, err := shell.ExecLookPath("git"); err != nil { + return fmt.Errorf("git ist nicht installiert") + } + + if _, err := shell.ExecLookPath("stow"); err != nil { + return fmt.Errorf("gnu stow not installed") + } + + dotfilesDir := filepath.Join(os.Getenv("HOME"), "dotfiles") + + cmd := shell.ExecCommand("git", "clone", config.GitRepo, dotfilesDir) + if err := cmd.Run(); err != nil { + return fmt.Errorf("error cloning dotfiles: %v", err) + } + + if err := os.Chdir(dotfilesDir); err != nil { + return fmt.Errorf("error changing into dotfiles directory: %v", err) + } + + cmd = shell.ExecCommand("stow", ".", "--override='*'") + if err := cmd.Run(); err != nil { + log.Printf("error creating links: %v", err) + } + fmt.Printf("all linked properly\n") + + return nil +} diff --git a/go.mod b/go.mod index f283e32..72088a7 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,7 @@ module system_setup_tool go 1.23.4 require ( - github.com/charmbracelet/bubbles v0.20.0 - github.com/charmbracelet/bubbletea v1.2.4 github.com/charmbracelet/huh v0.6.0 - github.com/charmbracelet/lipgloss v1.0.0 github.com/magefile/mage v1.15.0 github.com/mitchellh/mapstructure v1.5.0 github.com/schollz/progressbar/v3 v3.18.0 @@ -18,7 +15,9 @@ require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/catppuccin/go v0.2.0 // indirect - github.com/charmbracelet/harmonica v0.2.0 // indirect + github.com/charmbracelet/bubbles v0.20.0 // indirect + github.com/charmbracelet/bubbletea v1.2.4 // indirect + github.com/charmbracelet/lipgloss v1.0.0 // indirect github.com/charmbracelet/x/ansi v0.4.5 // indirect github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect diff --git a/go.sum b/go.sum index f3365b5..1146891 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,6 @@ github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQW github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE= github.com/charmbracelet/bubbletea v1.2.4/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM= -github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= -github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= github.com/charmbracelet/huh v0.6.0 h1:mZM8VvZGuE0hoDXq6XLxRtgfWyTI3b2jZNKh0xWmax8= github.com/charmbracelet/huh v0.6.0/go.mod h1:GGNKeWCeNzKpEOh/OJD8WBwTQjV3prFAtQPpLv+AVwU= github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= diff --git a/internal/shell/shell.go b/internal/shell/shell.go new file mode 100644 index 0000000..5b7a8b7 --- /dev/null +++ b/internal/shell/shell.go @@ -0,0 +1,23 @@ +package shell + +import ( + "fmt" + "os" + "os/exec" +) + +var ( + ExecCommand = exec.Command + ExecLookPath = exec.LookPath +) + +func ExecuteShellCommand(command string, env string) error { + cmd := ExecCommand("bash", "-c", command) + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, env) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("befehl fehlgeschlagen: %v\nAusgabe: %s", err, output) + } + return nil +} diff --git a/magefile.go b/magefile.go index 3603577..eb85b08 100644 --- a/magefile.go +++ b/magefile.go @@ -1,5 +1,4 @@ //go:build mage -// +build mage package main diff --git a/model.go b/model.go deleted file mode 100644 index 41cb2e6..0000000 --- a/model.go +++ /dev/null @@ -1,164 +0,0 @@ -package main - -import ( - "fmt" - "log" - "os" - "path/filepath" - "strings" - - "github.com/charmbracelet/bubbles/progress" - "github.com/charmbracelet/bubbles/spinner" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -type model struct { - packages []string - index int - width int - height int - spinner spinner.Model - progress progress.Model - done bool - sudoPassword string - os OS -} - -func (m model) installSpecialSoftware() error { - if _, err := execLookPath("oh-my-posh"); err == nil { - fmt.Println("Oh-my-posh ist bereits installiert") - } else { - poshCommand := "curl -s https://ohmyposh.dev/install.sh | bash -s" - if err := installPackage(poshCommand, "", ""); err != nil { - return err - } - } - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("Fehler beim Ermitteln des Home-Verzeichnisses: %v", err) - } - - ohMyZshDir := filepath.Join(homeDir, ".oh-my-zsh") - if _, err := os.Stat(ohMyZshDir); !os.IsNotExist(err) { - fmt.Println("Oh My Zsh ist bereits installiert.") - return nil - } - - fmt.Println("Installiere Oh My Zsh...") - err = executeShellCommand(`sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"`, "") - if err != nil { - log.Printf("Fehler bei der Installation von Oh My Zsh: %v\n", err) - } - - plugins := []string{ - "git clone https://github.com/zsh-users/zsh-autosuggestions.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/zsh-autosuggestions", - "git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting", - "git clone https://github.com/zdharma-continuum/fast-syntax-highlighting.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/fast-syntax-highlighting", - "git clone --depth 1 -- https://github.com/marlonrichert/zsh-autocomplete.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/zsh-autocomplete", - } - - for _, plugin := range plugins { - err := executeShellCommand(plugin, "") - if err != nil { - log.Printf("Fehler bei der Installation des Plugins: %v\n", err) - } - } - - fmt.Println("Oh My Zsh wurde erfolgreich installiert.") - return nil -} - -func newModel(packages []string, sudoPassword string, os *OS) model { - p := progress.New( - progress.WithDefaultGradient(), - progress.WithWidth(40), - progress.WithoutPercentage(), - ) - s := spinner.New() - s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("63")) - return model{ - packages: packages, - spinner: s, - progress: p, - sudoPassword: sudoPassword, - os: *os, - } -} - -func (m model) Init() tea.Cmd { - return tea.Batch(m.installPackageCmd(m.packages[m.index]), m.spinner.Tick) -} - -func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.WindowSizeMsg: - m.width, m.height = msg.Width, msg.Height - case tea.KeyMsg: - switch msg.String() { - case "ctrl+c", "esc", "q": - return m, tea.Quit - } - case installedPkgMsg: - pkg := m.packages[m.index] - if m.index >= len(m.packages)-1 { - m.done = true - return m, tea.Sequence( - tea.Printf("%s %s", checkMark, pkg), - tea.Quit, - ) - } - - m.index++ - progressCmd := m.progress.SetPercent(float64(m.index) / float64(len(m.packages))) - - return m, tea.Batch( - progressCmd, - tea.Printf("%s %s", checkMark, pkg), - m.installPackageCmd(m.packages[m.index]), - ) - case spinner.TickMsg: - var cmd tea.Cmd - m.spinner, cmd = m.spinner.Update(msg) - return m, cmd - case progress.FrameMsg: - newModel, cmd := m.progress.Update(msg) - if newModel, ok := newModel.(progress.Model); ok { - m.progress = newModel - } - return m, cmd - } - return m, nil -} - -func (m model) View() string { - n := len(m.packages) - w := lipgloss.Width(fmt.Sprintf("%d", n)) - - if m.done { - return doneStyle.Render(fmt.Sprintf("Done! Installed %d packages.\n", n)) - } - - pkgCount := fmt.Sprintf(" %*d/%*d", w, m.index, w, n) - - spin := m.spinner.View() + " " - prog := m.progress.View() - cellsAvail := max(0, m.width-lipgloss.Width(spin+prog+pkgCount)) - - pkgName := currentPkgNameStyle.Render(m.packages[m.index]) - info := lipgloss.NewStyle().MaxWidth(cellsAvail).Render("Installing " + pkgName) - - cellsRemaining := max(0, m.width-lipgloss.Width(spin+info+prog+pkgCount)) - gap := strings.Repeat(" ", cellsRemaining) - - return spin + info + gap + prog + pkgCount -} - -func (m model) installPackageCmd(pkg string) tea.Cmd { - return func() tea.Msg { - if err := installPackage(m.os.InstallCommand, pkg, m.sudoPassword); err != nil { - log.Printf("Fehler beim Installieren von %s: %v", pkg, err) - } - return installedPkgMsg(pkg) - } -} diff --git a/cargo.go b/packagemanager/cargo.go similarity index 60% rename from cargo.go rename to packagemanager/cargo.go index a5a80d8..9623a89 100644 --- a/cargo.go +++ b/packagemanager/cargo.go @@ -1,4 +1,6 @@ -package main +package packagemanager + +import "system_setup_tool/internal/shell" type CargoManager struct{} @@ -6,7 +8,7 @@ func (c *CargoManager) Install(packages []string) error { if len(packages) == 0 { return nil } - err := installWithProgress(c, packages) + err := InstallWithProgress(c, packages) if err != nil { return err } @@ -18,25 +20,25 @@ func (c *CargoManager) Name() string { } func (c *CargoManager) InstallManager() error { - if _, err := execLookPath("brew"); err != nil { + if _, err := shell.ExecLookPath("brew"); err != nil { installHomebrew() } - if _, err := execLookPath("cargo"); err == nil { + if _, err := shell.ExecLookPath("cargo"); err == nil { return nil } - cmd := execCommand("brew", "install", "rust") + cmd := shell.ExecCommand("brew", "install", "rust") return cmd.Run() } func (c *CargoManager) InstallPackage(pkg string) error { - cmd := execCommand("cargo", "install", pkg) + cmd := shell.ExecCommand("cargo", "install", pkg) return cmd.Run() } func (c *CargoManager) RemovePackage(pkg string) error { - cmd := execCommand("cargo", "uninstall", pkg) + cmd := shell.ExecCommand("cargo", "uninstall", pkg) return cmd.Run() } diff --git a/cargo_test.go b/packagemanager/cargo_test.go similarity index 72% rename from cargo_test.go rename to packagemanager/cargo_test.go index 5129f56..ef491de 100644 --- a/cargo_test.go +++ b/packagemanager/cargo_test.go @@ -1,8 +1,9 @@ // cargo_test.go -package main +package packagemanager import ( "os/exec" + "system_setup_tool/internal/shell" "testing" ) @@ -17,13 +18,13 @@ func TestCargoManager_InstallManager(t *testing.T) { cm := &CargoManager{} // Mock exec.LookPath - execLookPath = func(file string) (string, error) { + shell.ExecLookPath = func(file string) (string, error) { if file == "cargo" { return "/usr/bin/cargo", nil } return "", exec.ErrNotFound } - defer func() { execLookPath = exec.LookPath }() + defer func() { shell.ExecLookPath = exec.LookPath }() if err := cm.InstallManager(); err != nil { t.Errorf("Expected no error, got %v", err) @@ -34,10 +35,10 @@ func TestCargoManager_InstallPackage(t *testing.T) { cm := &CargoManager{} // Mock exec.Command - execCommand = func(name string, arg ...string) *exec.Cmd { + shell.ExecCommand = func(name string, arg ...string) *exec.Cmd { return exec.Command("echo", "mocked cargo install") } - defer func() { execCommand = exec.Command }() + defer func() { shell.ExecCommand = exec.Command }() if err := cm.InstallPackage("test-package"); err != nil { t.Errorf("Expected no error, got %v", err) diff --git a/flatpak.go b/packagemanager/flatpak.go similarity index 71% rename from flatpak.go rename to packagemanager/flatpak.go index 0c926bc..4c30842 100644 --- a/flatpak.go +++ b/packagemanager/flatpak.go @@ -1,10 +1,10 @@ -package main +package packagemanager import ( "fmt" "log" - "os/exec" "strings" + "system_setup_tool/internal/shell" ) type FlatpakManager struct { @@ -13,11 +13,23 @@ type FlatpakManager struct { Config FlatpakConfig } +func NewFlatpakManager(sudoPassword string, config FlatpakConfig) *FlatpakManager { + os, err := GetLinuxDistribution() + if err != nil { + log.Fatalf("error geting os information: %v", err) + } + return &FlatpakManager{ + OS: os, + SudoPassword: sudoPassword, + Config: config, + } +} + func (f *FlatpakManager) Install(packages []string) error { if len(packages) == 0 { return nil } - err := installWithProgress(f, packages) + err := InstallWithProgress(f, packages) if err != nil { return err } @@ -29,7 +41,7 @@ func (f *FlatpakManager) Name() string { } func (f *FlatpakManager) InstallManager() error { - if _, err := exec.LookPath("flatpak"); err == nil { + if _, err := shell.ExecLookPath("flatpak"); err == nil { return nil } @@ -69,7 +81,7 @@ func installFlatpak(os *OS, sudoPassword string) error { return fmt.Errorf("keine Flatpak-Installation für OS %s definiert", os.ID) } - if err := installPackage(command, "", sudoPassword); err != nil { + if err := InstallPackage(command, "", sudoPassword); err != nil { return fmt.Errorf("fehler bei der Flatpak-Installation: %v", err) } return nil @@ -77,7 +89,7 @@ func installFlatpak(os *OS, sudoPassword string) error { func addFlatpakRemotes(remotes []Remote) error { for _, remote := range remotes { - cmd := execCommand("flatpak", "remote-add", "--if-not-exists", remote.Name, remote.URL) + cmd := shell.ExecCommand("flatpak", "remote-add", "--if-not-exists", remote.Name, remote.URL) if err := cmd.Run(); err != nil { return fmt.Errorf("fehler beim Hinzufügen des Remotes %s: %v", remote.Name, err) } @@ -86,17 +98,17 @@ func addFlatpakRemotes(remotes []Remote) error { } func (f *FlatpakManager) InstallPackage(pkg string) error { - cmd := execCommand("flatpak", "install", "-y", pkg) + cmd := shell.ExecCommand("flatpak", "install", "-y", pkg) return cmd.Run() } func (f *FlatpakManager) RemovePackage(pkg string) error { - cmd := execCommand("flatpak", "uninstall", "-y", pkg) + cmd := shell.ExecCommand("flatpak", "uninstall", "-y", pkg) return cmd.Run() } func (f *FlatpakManager) SearchPackage(pkg string) []string { - cmd := execCommand("flatpak", "search", pkg) + cmd := shell.ExecCommand("flatpak", "search", pkg) packages, err := cmd.Output() if err != nil { log.Printf("error fetching %s packages: %v", f.Name(), err) diff --git a/flatpak_test.go b/packagemanager/flatpak_test.go similarity index 83% rename from flatpak_test.go rename to packagemanager/flatpak_test.go index c99e2d5..96fad8f 100644 --- a/flatpak_test.go +++ b/packagemanager/flatpak_test.go @@ -1,8 +1,8 @@ -// flatpak_test.go -package main +package packagemanager import ( "os/exec" + "system_setup_tool/internal/shell" "testing" ) @@ -33,10 +33,10 @@ func TestFlatpakManager_InstallPackage(t *testing.T) { fm := &FlatpakManager{} // Mock exec.Command - execCommand = func(name string, arg ...string) *exec.Cmd { + shell.ExecCommand = func(name string, arg ...string) *exec.Cmd { return exec.Command("echo", "mocked flatpak install") } - defer func() { execCommand = exec.Command }() + defer func() { shell.ExecCommand = exec.Command }() if err := fm.InstallPackage("test-package"); err != nil { t.Errorf("Expected no error, got %v", err) diff --git a/golang.go b/packagemanager/golang.go similarity index 60% rename from golang.go rename to packagemanager/golang.go index 690c2d4..9392f89 100644 --- a/golang.go +++ b/packagemanager/golang.go @@ -1,4 +1,6 @@ -package main +package packagemanager + +import "system_setup_tool/internal/shell" type GolangManager struct{} @@ -6,7 +8,7 @@ func (g *GolangManager) Install(packages []string) error { if len(packages) == 0 { return nil } - err := installWithProgress(g, packages) + err := InstallWithProgress(g, packages) if err != nil { return err } @@ -18,25 +20,25 @@ func (g *GolangManager) Name() string { } func (g *GolangManager) InstallManager() error { - if _, err := execLookPath("brew"); err != nil { + if _, err := shell.ExecLookPath("brew"); err != nil { installHomebrew() } - if _, err := execLookPath("go"); err == nil { + if _, err := shell.ExecLookPath("go"); err == nil { return nil } - cmd := execCommand("brew", "install", "golang") + cmd := shell.ExecCommand("brew", "install", "golang") return cmd.Run() } func (g *GolangManager) InstallPackage(pkg string) error { - cmd := execCommand("go", "install", pkg+"@latest") + cmd := shell.ExecCommand("go", "install", pkg+"@latest") return cmd.Run() } func (g *GolangManager) RemovePackage(pkg string) error { - cmd := execCommand("go", "uninstall", pkg) + cmd := shell.ExecCommand("go", "uninstall", pkg) return cmd.Run() } diff --git a/golang_test.go b/packagemanager/golang_test.go similarity index 72% rename from golang_test.go rename to packagemanager/golang_test.go index 5e67b0a..930fe0b 100644 --- a/golang_test.go +++ b/packagemanager/golang_test.go @@ -1,8 +1,8 @@ -// golang_test.go -package main +package packagemanager import ( "os/exec" + "system_setup_tool/internal/shell" "testing" ) @@ -17,13 +17,13 @@ func TestGolangManager_InstallManager(t *testing.T) { gm := &GolangManager{} // Mock exec.LookPath - execLookPath = func(file string) (string, error) { + shell.ExecLookPath = func(file string) (string, error) { if file == "go" { return "/usr/local/go/bin/go", nil } return "", exec.ErrNotFound } - defer func() { execLookPath = exec.LookPath }() + defer func() { shell.ExecLookPath = exec.LookPath }() if err := gm.InstallManager(); err != nil { t.Errorf("Expected no error, got %v", err) @@ -34,10 +34,10 @@ func TestGolangManager_InstallPackage(t *testing.T) { gm := &GolangManager{} // Mock exec.Command - execCommand = func(name string, arg ...string) *exec.Cmd { + shell.ExecCommand = func(name string, arg ...string) *exec.Cmd { return exec.Command("echo", "mocked go install") } - defer func() { execCommand = exec.Command }() + defer func() { shell.ExecCommand = exec.Command }() if err := gm.InstallPackage("github.com/test/package"); err != nil { t.Errorf("Expected no error, got %v", err) diff --git a/homebrew.go b/packagemanager/homebrew.go similarity index 68% rename from homebrew.go rename to packagemanager/homebrew.go index 9cd259d..93cbcd6 100644 --- a/homebrew.go +++ b/packagemanager/homebrew.go @@ -1,9 +1,10 @@ -package main +package packagemanager import ( "fmt" "log" "strings" + "system_setup_tool/internal/shell" ) type HomebrewManager struct{} @@ -12,7 +13,7 @@ func (h *HomebrewManager) Install(packages []string) error { if len(packages) == 0 { return nil } - err := installWithProgress(h, packages) + err := InstallWithProgress(h, packages) if err != nil { return err } @@ -24,7 +25,7 @@ func (h *HomebrewManager) Name() string { } func (h *HomebrewManager) InstallManager() error { - if _, err := execLookPath("brew"); err == nil { + if _, err := shell.ExecLookPath("brew"); err == nil { return nil } @@ -32,7 +33,7 @@ func (h *HomebrewManager) InstallManager() error { } func installHomebrew() error { - err := executeShellCommand("sh -c $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)", "NONINTERACTE=1") + err := shell.ExecuteShellCommand("sh -c $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)", "NONINTERACTE=1") if err != nil { return fmt.Errorf("fehler bei der Installation von Homebrew: %v", err) } @@ -40,17 +41,17 @@ func installHomebrew() error { } func (h *HomebrewManager) InstallPackage(pkg string) error { - cmd := execCommand("brew", "install", pkg) + cmd := shell.ExecCommand("brew", "install", pkg) return cmd.Run() } func (h *HomebrewManager) RemovePackage(pkg string) error { - cmd := execCommand("brew", "uninstall", pkg) + cmd := shell.ExecCommand("brew", "uninstall", pkg) return cmd.Run() } func (h *HomebrewManager) SearchPackage(pkg string) []string { - cmd := execCommand("brew", "search", pkg) + cmd := shell.ExecCommand("brew", "search", pkg) packages, err := cmd.Output() if err != nil { log.Printf("error fetching %s packages: %v", h.Name(), err) diff --git a/homebrew_test.go b/packagemanager/homebrew_test.go similarity index 73% rename from homebrew_test.go rename to packagemanager/homebrew_test.go index 72da6ee..14128f0 100644 --- a/homebrew_test.go +++ b/packagemanager/homebrew_test.go @@ -1,8 +1,8 @@ -// homebrew_test.go -package main +package packagemanager import ( "os/exec" + "system_setup_tool/internal/shell" "testing" ) @@ -17,13 +17,13 @@ func TestHomebrewManager_InstallManager(t *testing.T) { hm := &HomebrewManager{} // Mock exec.LookPath and executeShellCommand - execLookPath = func(file string) (string, error) { + shell.ExecLookPath = func(file string) (string, error) { if file == "brew" { return "/usr/local/bin/brew", nil } return "", exec.ErrNotFound } - defer func() { execLookPath = exec.LookPath }() + defer func() { shell.ExecLookPath = exec.LookPath }() if err := hm.InstallManager(); err != nil { t.Errorf("Expected no error, got %v", err) @@ -34,10 +34,10 @@ func TestHomebrewManager_InstallPackage(t *testing.T) { hm := &HomebrewManager{} // Mock exec.Command - execCommand = func(name string, arg ...string) *exec.Cmd { + shell.ExecCommand = func(name string, arg ...string) *exec.Cmd { return exec.Command("echo", "mocked brew install") } - defer func() { execCommand = exec.Command }() + defer func() { shell.ExecCommand = exec.Command }() if err := hm.InstallPackage("test-package"); err != nil { t.Errorf("Expected no error, got %v", err) diff --git a/osinfo.go b/packagemanager/os.go similarity index 81% rename from osinfo.go rename to packagemanager/os.go index bba7397..e337347 100644 --- a/osinfo.go +++ b/packagemanager/os.go @@ -1,27 +1,26 @@ -package main +package packagemanager import ( "fmt" "log" "os" "strings" - - tea "github.com/charmbracelet/bubbletea" + "system_setup_tool/internal/shell" ) type OSManager struct { OS *OS SudoPassword string - Packages []string - Model model } -func NewOSManager(os *OS, sudoPassword string, packages []string) *OSManager { +func NewOSManager(sudoPassword string) *OSManager { + os, err := GetLinuxDistribution() + if err != nil { + log.Fatalf("error geting os information: %v", err) + } return &OSManager{ OS: os, SudoPassword: sudoPassword, - Packages: packages, - Model: newModel(packages, sudoPassword, os), } } @@ -32,42 +31,39 @@ func (o *OSManager) Name() string { func (o *OSManager) InstallManager() error { return nil } func (o *OSManager) Install(packages []string) error { - o.Packages = packages - o.Model = newModel(packages, o.SudoPassword, o.OS) - - p := tea.NewProgram(o.Model) - _, err := p.Run() - return err + if len(packages) == 0 { + return nil + } + err := InstallWithProgress(o, packages) + if err != nil { + return err + } + return nil } func (o *OSManager) InstallPackage(pkg string) error { - return installPackage(o.OS.InstallCommand, pkg, o.SudoPassword) + return InstallPackage(o.OS.InstallCommand, pkg, o.SudoPassword) } func (o *OSManager) InstallBuildEssentials() error { - return installBuildEssentials(o.OS, o.SudoPassword) -} - -func (o *OSManager) InstallSpecialSoftware() error { - return o.Model.installSpecialSoftware() + return InstallBuildEssentials(o.OS, o.SudoPassword) } func (o *OSManager) RemovePackage(pkg string) error { fullCmd := fmt.Sprintf("%s %s", o.OS.RemoveCommand, pkg) - command := execCommand("sudo", "-S", "sh", "-c", fullCmd) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) command.Stdin = strings.NewReader(o.SudoPassword + "\n") return command.Run() } func (o *OSManager) SearchPackage(pkg string) []string { - cmdParts := strings.Fields(o.OS.SearchCommand) if len(cmdParts) == 0 { log.Printf("Invalid search command for OS package manager") return []string{} } - cmd := execCommand(cmdParts[0], append(cmdParts[1:], pkg)...) + cmd := shell.ExecCommand(cmdParts[0], append(cmdParts[1:], pkg)...) packages, err := cmd.Output() if err != nil { log.Printf("Error fetching %s packages: %v", o.OS.PackageManager, err) @@ -127,7 +123,7 @@ func parseOsRelease(osRelease string) *OS { return &result } -func getLinuxDistribution() (*OS, error) { +func GetLinuxDistribution() (*OS, error) { _, err := os.Stat("/etc/os-release") if os.IsNotExist(err) { return nil, fmt.Errorf("unable to read system information") @@ -155,7 +151,7 @@ func (os *OS) getPackageManager() error { "pacman", } for _, pmname := range pmcommands { - _, err := execLookPath(pmname) + _, err := shell.ExecLookPath(pmname) if err == nil { os.PackageManager = pmname return nil @@ -213,7 +209,7 @@ func (os *OS) getDeleteCommand() error { } } -func installBuildEssentials(os *OS, sudoPassword string) error { +func InstallBuildEssentials(os *OS, sudoPassword string) error { var command string switch os.PackageManager { case "pacman": @@ -227,8 +223,8 @@ func installBuildEssentials(os *OS, sudoPassword string) error { } fmt.Printf("Installiere Build Essentials für %s...\n", os.Name) - if err := installPackage(command, "", sudoPassword); err != nil { - return fmt.Errorf("fehler bei der Installation der Build Essentials: %v", err) + if err := InstallPackage(command, "", sudoPassword); err != nil { + return fmt.Errorf("error installing Build Essentials: %v", err) } return nil } diff --git a/osinfo_test.go b/packagemanager/os_test.go similarity index 63% rename from osinfo_test.go rename to packagemanager/os_test.go index ddca2ec..25dbf58 100644 --- a/osinfo_test.go +++ b/packagemanager/os_test.go @@ -1,26 +1,20 @@ -// osmanager_test.go -package main +package packagemanager import ( "testing" ) func TestNewOSManager(t *testing.T) { - os := &OS{ID: "ubuntu", PackageManager: "apt"} sudoPassword := "testpassword" - packages := []string{"git", "curl"} - manager := NewOSManager(os, sudoPassword, packages) + manager := NewOSManager(sudoPassword) - if manager.OS != os { - t.Errorf("Expected OS to be %v, got %v", os, manager.OS) + if manager.OS == nil { + t.Error("Expected OS to be non-nil") } if manager.SudoPassword != sudoPassword { t.Errorf("Expected SudoPassword to be %s, got %s", sudoPassword, manager.SudoPassword) } - if len(manager.Packages) != len(packages) { - t.Errorf("Expected Packages length to be %d, got %d", len(packages), len(manager.Packages)) - } } func TestOSManagerName(t *testing.T) { @@ -47,4 +41,13 @@ VERSION_ID="20.04" if os.Version != "20.04" { t.Errorf("Expected Version to be '20.04', got %s", os.Version) } + if os.PackageManager != "apt" { + t.Errorf("Expected PackageManager to be 'apt', got %s", os.PackageManager) + } + if os.InstallCommand != "apt install -y" { + t.Errorf("Expected InstallCommand to be 'apt install -y', got %s", os.InstallCommand) + } + if os.SearchCommand != "apt search" { + t.Errorf("Expected SearchCommand to be 'apt search', got %s", os.SearchCommand) + } } diff --git a/package.go b/packagemanager/package.go similarity index 62% rename from package.go rename to packagemanager/package.go index 77b7a46..ca38da6 100644 --- a/package.go +++ b/packagemanager/package.go @@ -1,8 +1,10 @@ -package main +package packagemanager import ( "fmt" + "log" "strings" + "system_setup_tool/internal/shell" ) type Packages struct { @@ -10,26 +12,17 @@ type Packages struct { NonHeadless []string `mapstructure:"non_headless"` } -func installPackage(cmd, pkg, sudoPassword string) error { +func InstallPackage(cmd, pkg, sudoPassword string) error { fullCmd := fmt.Sprintf("%s %s", cmd, pkg) - command := execCommand("sudo", "-S", "sh", "-c", fullCmd) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) command.Stdin = strings.NewReader(sudoPassword + "\n") output, err := command.CombinedOutput() if err != nil { if strings.Contains(string(output), "not found") || strings.Contains(string(output), "no matching package") || strings.Contains(string(output), "Keine Übereinstimmung") || strings.Contains(string(output), "Ziel nicht gefunden") { - unavailablePackages = append(unavailablePackages, pkg) + log.Printf("Package %s not available\n", pkg) return nil } return fmt.Errorf("failed to install %s: %v\n%s", pkg, err, string(output)) } return nil } - -func printUnavailablePackages() { - if len(unavailablePackages) > 0 { - fmt.Println("\nFolgende Pakete waren nicht verfügbar:") - for _, pkg := range unavailablePackages { - fmt.Printf("- %s\n", pkg) - } - } -} diff --git a/packagemanager.go b/packagemanager/packagemanager.go similarity index 90% rename from packagemanager.go rename to packagemanager/packagemanager.go index a5f17c2..48362d2 100644 --- a/packagemanager.go +++ b/packagemanager/packagemanager.go @@ -1,4 +1,4 @@ -package main +package packagemanager type PackageManager interface { Install(packages []string) error diff --git a/pipx.go b/packagemanager/pipx.go similarity index 60% rename from pipx.go rename to packagemanager/pipx.go index 8f6820a..a4e1716 100644 --- a/pipx.go +++ b/packagemanager/pipx.go @@ -1,4 +1,6 @@ -package main +package packagemanager + +import "system_setup_tool/internal/shell" type PipxManager struct{} @@ -7,7 +9,7 @@ func (p *PipxManager) Install(packages []string) error { return nil } - err := installWithProgress(p, packages) + err := InstallWithProgress(p, packages) if err != nil { return err } @@ -19,25 +21,25 @@ func (p *PipxManager) Name() string { } func (p *PipxManager) InstallManager() error { - if _, err := execLookPath("brew"); err != nil { + if _, err := shell.ExecLookPath("brew"); err != nil { installHomebrew() } - if _, err := execLookPath("pipx"); err == nil { + if _, err := shell.ExecLookPath("pipx"); err == nil { return nil } - cmd := execCommand("brew", "install", "pipx") + cmd := shell.ExecCommand("brew", "install", "pipx") return cmd.Run() } func (p *PipxManager) InstallPackage(pkg string) error { - cmd := execCommand("pipx", "install", pkg) + cmd := shell.ExecCommand("pipx", "install", pkg) return cmd.Run() } func (p *PipxManager) RemovePackage(pkg string) error { - cmd := execCommand("pipx", "uninstall", pkg) + cmd := shell.ExecCommand("pipx", "uninstall", pkg) return cmd.Run() } diff --git a/pipx_test.go b/packagemanager/pipx_test.go similarity index 71% rename from pipx_test.go rename to packagemanager/pipx_test.go index a7f7389..b87fb2c 100644 --- a/pipx_test.go +++ b/packagemanager/pipx_test.go @@ -1,8 +1,8 @@ -// pipx_test.go -package main +package packagemanager import ( "os/exec" + "system_setup_tool/internal/shell" "testing" ) @@ -17,13 +17,13 @@ func TestPipxManager_InstallManager(t *testing.T) { pm := &PipxManager{} // Mock exec.LookPath - execLookPath = func(file string) (string, error) { + shell.ExecLookPath = func(file string) (string, error) { if file == "pipx" { return "/usr/bin/pipx", nil } return "", exec.ErrNotFound } - defer func() { execLookPath = exec.LookPath }() + defer func() { shell.ExecLookPath = exec.LookPath }() if err := pm.InstallManager(); err != nil { t.Errorf("Expected no error, got %v", err) @@ -34,10 +34,10 @@ func TestPipxManager_InstallPackage(t *testing.T) { pm := &PipxManager{} // Mock exec.Command - execCommand = func(name string, arg ...string) *exec.Cmd { + shell.ExecCommand = func(name string, arg ...string) *exec.Cmd { return exec.Command("echo", "mocked pipx install") } - defer func() { execCommand = exec.Command }() + defer func() { shell.ExecCommand = exec.Command }() if err := pm.InstallPackage("test-package"); err != nil { t.Errorf("Expected no error, got %v", err) diff --git a/packagemanager/utils.go b/packagemanager/utils.go new file mode 100644 index 0000000..293679c --- /dev/null +++ b/packagemanager/utils.go @@ -0,0 +1,19 @@ +package packagemanager + +import ( + "log" + + "github.com/schollz/progressbar/v3" +) + +func InstallWithProgress(manager PackageManager, packages []string) error { + bar := progressbar.Default(int64(len(packages)), "Installiere "+manager.Name()+"-Pakete...") + for _, pkg := range packages { + err := manager.InstallPackage(pkg) + if err != nil { + log.Printf("\nError installing %s: %v\n", pkg, err) + } + bar.Add(1) + } + return nil +} diff --git a/tui.go b/tui.go deleted file mode 100644 index ab1be10..0000000 --- a/tui.go +++ /dev/null @@ -1,146 +0,0 @@ -package main - -import ( - "fmt" - "log" - - "github.com/charmbracelet/huh" - "github.com/charmbracelet/lipgloss" - "github.com/mitchellh/mapstructure" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -var ( - currentPkgNameStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("211")) - doneStyle = lipgloss.NewStyle().Margin(1, 2) - checkMark = lipgloss.NewStyle().Foreground(lipgloss.Color("42")).SetString("✓") - unavailablePackages []string -) - -type installedPkgMsg string - -func run(cmd *cobra.Command, args []string) { - os, err := getLinuxDistribution() - if err != nil { - log.Fatal(err) - } - - sudoPassword, err := getSudoPassword() - if err != nil { - log.Fatal(err) - } - - var cfg Config - if err := viper.Unmarshal(&cfg); err != nil { - log.Fatalf("Fehler beim Lesen der Konfiguration: %v", err) - } - - form := huh.NewForm( - huh.NewGroup( - huh.NewConfirm(). - Title("Möchten Sie eine headless Installation durchführen?"). - Value(&cfg.Headless), - ), - ).WithTheme(huh.ThemeCatppuccin()) - - if err := form.Run(); err != nil { - log.Fatalf("Fehler bei der Benutzerabfrage: %v", err) - } - - unavailablePackages = []string{} - osManager := NewOSManager(os, sudoPassword, cfg.Packages.Headless) - - if err := osManager.Install(cfg.Packages.Headless); err != nil { - log.Printf("Warnung bei der Installation der Headless-Pakete: %v", err) - } - if !cfg.Headless { - if err := osManager.Install(cfg.Packages.NonHeadless); err != nil { - log.Printf("Warnung bei der Installation der Non-Headless-Pakete: %v", err) - } - } - - printUnavailablePackages() - - var installBuild bool - form = huh.NewForm( - huh.NewGroup( - huh.NewConfirm(). - Title("Möchten Sie die Build Essentials installieren?"). - Value(&installBuild), - ), - ).WithTheme(huh.ThemeCatppuccin()) - - err = form.Run() - if err != nil { - log.Fatal(err) - } - - if installBuild { - if err := osManager.InstallBuildEssentials(); err != nil { - log.Printf("Warnung: %v", err) - } - } - - var installShellExtensions bool - form = huh.NewForm( - huh.NewGroup( - huh.NewConfirm(). - Title("Möchten Sie die Shell-Extensions installieren?"). - Value(&installShellExtensions), - ), - ).WithTheme(huh.ThemeCatppuccin()) - - err = form.Run() - if err != nil { - log.Fatal(err) - } - if installShellExtensions { - if err := osManager.InstallSpecialSoftware(); err != nil { - log.Printf("Warnung bei der Installation spezieller Software: %v", err) - } - } - - for name, config := range cfg.PackageManagers { - if config.Enable { - var manager PackageManager - switch name { - case "homebrew": - manager = &HomebrewManager{} - case "go": - manager = &GolangManager{} - case "cargo": - manager = &CargoManager{} - case "pipx": - manager = &PipxManager{} - case "flatpak": - flatpakConfig := FlatpakConfig{} - if err := mapstructure.Decode(config, &flatpakConfig); err != nil { - log.Printf("Fehler beim Dekodieren der Flatpak-Konfiguration: %v", err) - continue - } - manager = &FlatpakManager{ - OS: os, - SudoPassword: sudoPassword, - Config: flatpakConfig, - } - default: - log.Printf("Unbekannter Paketmanager: %s", name) - continue - } - if err := manager.InstallManager(); err != nil { - log.Printf("Warnung Packagemanager %s nicht vorhanden und konnte nicht installiert werden: %v", manager.Name(), err) - } - - if err := manager.Install(config.Packages); err != nil { - log.Printf("Warnung bei %s-Paketen: %v", manager.Name(), err) - } - } - } - if cfg.Dotfiles.Enable { - fmt.Println("\nKonfiguriere Dotfiles...") - if err := setupDotfiles(cfg.Dotfiles); err != nil { - log.Printf("Warnung bei Dotfiles-Setup: %v", err) - } - } -} diff --git a/tui/tui.go b/tui/tui.go new file mode 100644 index 0000000..a51004e --- /dev/null +++ b/tui/tui.go @@ -0,0 +1,115 @@ +package tui + +import ( + "fmt" + "log" + "system_setup_tool/config" + "system_setup_tool/dotfiles" + "system_setup_tool/utils" + + pm "system_setup_tool/packagemanager" + + "github.com/charmbracelet/huh" + "github.com/mitchellh/mapstructure" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +type installedPkgMsg string + +func Run(cmd *cobra.Command, args []string) { + sudoPassword, err := utils.GetSudoPassword() + if err != nil { + log.Fatal(err) + } + + var cfg config.Config + if err := viper.Unmarshal(&cfg); err != nil { + log.Fatalf("Fehler beim Lesen der Konfiguration: %v", err) + } + + // form := huh.NewForm( + // huh.NewGroup( + // huh.NewConfirm(). + // Title("Möchten Sie eine headless Installation durchführen?"). + // Value(&cfg.Headless), + // ), + // ).WithTheme(huh.ThemeCatppuccin()) + // + // if err := form.Run(); err != nil { + // log.Fatalf("Fehler bei der Benutzerabfrage: %v", err) + // } + // + // osManager := pm.NewOSManager(sudoPassword) + // + // if err := osManager.Install(cfg.Packages.Headless); err != nil { + // log.Printf("Warnung bei der Installation der Headless-Pakete: %v", err) + // } + // if !cfg.Headless { + // if err := osManager.Install(cfg.Packages.NonHeadless); err != nil { + // log.Printf("Warnung bei der Installation der Non-Headless-Pakete: %v", err) + // } + // } + // + + for name, config := range cfg.PackageManagers { + if config.Enable { + var manager pm.PackageManager + switch name { + case "os": + manager = pm.NewOSManager(sudoPassword) + case "homebrew": + manager = &pm.HomebrewManager{} + case "go": + manager = &pm.GolangManager{} + case "cargo": + manager = &pm.CargoManager{} + case "pipx": + manager = &pm.PipxManager{} + case "flatpak": + flatpakConfig := pm.FlatpakConfig{} + if err := mapstructure.Decode(config, &flatpakConfig); err != nil { + log.Printf("error decoding flatpak config: %v", err) + continue + } + manager = pm.NewFlatpakManager(sudoPassword, flatpakConfig) + default: + log.Printf("unknown packagemanager: %s", name) + continue + } + if err := manager.InstallManager(); err != nil { + log.Printf("warning packagemanager %s not installed and could not be installed: %v", manager.Name(), err) + } + + if err := manager.Install(config.Packages); err != nil { + log.Printf("warning at %s-packages: %v", manager.Name(), err) + } + } + } + osManager := pm.NewOSManager(sudoPassword) + var installBuild bool + form := huh.NewForm( + huh.NewGroup( + huh.NewConfirm(). + Title("Möchten Sie die Build Essentials installieren?"). + Value(&installBuild), + ), + ).WithTheme(huh.ThemeCatppuccin()) + + err = form.Run() + if err != nil { + log.Fatal(err) + } + + if installBuild { + if err := osManager.InstallBuildEssentials(); err != nil { + log.Printf("Warnung: %v", err) + } + } + if cfg.Dotfiles.Enable { + fmt.Println("\nconfiguring dotfiles...") + if err := dotfiles.SetupDotfiles(cfg.Dotfiles); err != nil { + log.Printf("Warnung bei Dotfiles-Setup: %v", err) + } + } +} diff --git a/utils.go b/utils.go deleted file mode 100644 index 58f6896..0000000 --- a/utils.go +++ /dev/null @@ -1,64 +0,0 @@ -package main - -import ( - "fmt" - "log" - "os" - "os/exec" - - "github.com/charmbracelet/huh" - "github.com/schollz/progressbar/v3" -) - -var ( - execCommand = exec.Command - execLookPath = exec.LookPath -) - -func getSudoPassword() (string, error) { - var password string - form := huh.NewForm( - huh.NewGroup( - huh.NewInput(). - Title("Bitte geben Sie Ihr sudo-Passwort ein"). - EchoMode(huh.EchoModePassword). - Value(&password), - ), - ).WithTheme(huh.ThemeCatppuccin()) - - err := form.Run() - if err != nil { - return "", fmt.Errorf("fehler bei der Passwortabfrage: %v", err) - } - return password, nil -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} - -func executeShellCommand(command string, env string) error { - cmd := execCommand("bash", "-c", command) - cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, env) - output, err := cmd.CombinedOutput() - if err != nil { - return fmt.Errorf("befehl fehlgeschlagen: %v\nAusgabe: %s", err, output) - } - return nil -} - -func installWithProgress(manager PackageManager, packages []string) error { - bar := progressbar.Default(int64(len(packages)), "Installiere "+manager.Name()+"-Pakete...") - for _, pkg := range packages { - err := manager.InstallPackage(pkg) - if err != nil { - log.Printf("\nError installing %s: %v\n", pkg, err) - } - bar.Add(1) - } - return nil -} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..9024846 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,32 @@ +package utils + +import ( + "fmt" + + "github.com/charmbracelet/huh" +) + +func GetSudoPassword() (string, error) { + var password string + form := huh.NewForm( + huh.NewGroup( + huh.NewInput(). + Title("Bitte geben Sie Ihr sudo-Passwort ein"). + EchoMode(huh.EchoModePassword). + Value(&password), + ), + ).WithTheme(huh.ThemeCatppuccin()) + + err := form.Run() + if err != nil { + return "", fmt.Errorf("fehler bei der Passwortabfrage: %v", err) + } + return password, nil +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/utils/utils_test.go b/utils/utils_test.go new file mode 100644 index 0000000..243432e --- /dev/null +++ b/utils/utils_test.go @@ -0,0 +1,40 @@ +package utils + +import ( + "os/exec" + "system_setup_tool/internal/shell" + "testing" +) + +func TestMax(t *testing.T) { + tests := []struct { + a, b, want int + }{ + {1, 2, 2}, + {5, 3, 5}, + {0, 0, 0}, + {-1, -5, -1}, + } + + for _, tt := range tests { + got := max(tt.a, tt.b) + if got != tt.want { + t.Errorf("max(%d, %d) = %d; want %d", tt.a, tt.b, got, tt.want) + } + } +} + +func TestExecuteShellCommand(t *testing.T) { + // Mock execCommand + oldExecCommand := shell.ExecCommand + defer func() { shell.ExecCommand = oldExecCommand }() + + shell.ExecCommand = func(command string, args ...string) *exec.Cmd { + return exec.Command("echo", "mocked command") + } + + err := shell.ExecuteShellCommand("test command", "TEST_ENV=value") + if err != nil { + t.Errorf("executeShellCommand() error = %v; want nil", err) + } +} diff --git a/utils_test.go b/utils_test.go deleted file mode 100644 index ffd2eaa..0000000 --- a/utils_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package main - -import ( - "os/exec" - "testing" -) - -func TestMax(t *testing.T) { - tests := []struct { - a, b, want int - }{ - {1, 2, 2}, - {5, 3, 5}, - {0, 0, 0}, - {-1, -5, -1}, - } - - for _, tt := range tests { - got := max(tt.a, tt.b) - if got != tt.want { - t.Errorf("max(%d, %d) = %d; want %d", tt.a, tt.b, got, tt.want) - } - } -} - -func TestExecuteShellCommand(t *testing.T) { - // Mock execCommand - oldExecCommand := execCommand - defer func() { execCommand = oldExecCommand }() - - execCommand = func(command string, args ...string) *exec.Cmd { - return exec.Command("echo", "mocked command") - } - - err := executeShellCommand("test command", "TEST_ENV=value") - if err != nil { - t.Errorf("executeShellCommand() error = %v; want nil", err) - } -} - -func TestInstallWithProgress(t *testing.T) { - mockManager := &MockPackageManager{ - packages: []string{"pkg1", "pkg2"}, - } - - err := installWithProgress(mockManager, mockManager.packages) - if err != nil { - t.Errorf("installWithProgress() error = %v; want nil", err) - } - - if len(mockManager.installedPackages) != len(mockManager.packages) { - t.Errorf("installWithProgress() installed %d packages; want %d", len(mockManager.installedPackages), len(mockManager.packages)) - } -} - -type MockPackageManager struct { - packages []string - installedPackages []string -} - -func (m *MockPackageManager) Install(packages []string) error { - m.installedPackages = append(m.installedPackages, packages...) - return nil -} - -func (m *MockPackageManager) InstallPackage(pkg string) error { - m.installedPackages = append(m.installedPackages, pkg) - return nil -} - -func (m *MockPackageManager) InstallManager() error { - return nil -} - -func (m *MockPackageManager) Name() string { - return "MockManager" -} - -func (m *MockPackageManager) RemovePackage(pkg string) error { - return nil -} - -func (m *MockPackageManager) SearchPackage(pkg string) []string { - return nil -}