refactor: seperate files, function and structs into seperate packages

This commit is contained in:
Patryk Hegenberg 2025-01-18 14:34:16 +01:00
parent 7f951585c8
commit 5b7775f33e
37 changed files with 547 additions and 677 deletions

47
packagemanager/cargo.go Normal file
View file

@ -0,0 +1,47 @@
package packagemanager
import "system_setup_tool/internal/shell"
type CargoManager struct{}
func (c *CargoManager) Install(packages []string) error {
if len(packages) == 0 {
return nil
}
err := InstallWithProgress(c, packages)
if err != nil {
return err
}
return nil
}
func (c *CargoManager) Name() string {
return "Cargo"
}
func (c *CargoManager) InstallManager() error {
if _, err := shell.ExecLookPath("brew"); err != nil {
installHomebrew()
}
if _, err := shell.ExecLookPath("cargo"); err == nil {
return nil
}
cmd := shell.ExecCommand("brew", "install", "rust")
return cmd.Run()
}
func (c *CargoManager) InstallPackage(pkg string) error {
cmd := shell.ExecCommand("cargo", "install", pkg)
return cmd.Run()
}
func (c *CargoManager) RemovePackage(pkg string) error {
cmd := shell.ExecCommand("cargo", "uninstall", pkg)
return cmd.Run()
}
func (c *CargoManager) SearchPackage(pkg string) []string {
return []string{}
}

View file

@ -0,0 +1,46 @@
// cargo_test.go
package packagemanager
import (
"os/exec"
"system_setup_tool/internal/shell"
"testing"
)
func TestCargoManager_Name(t *testing.T) {
cm := &CargoManager{}
if name := cm.Name(); name != "Cargo" {
t.Errorf("Expected name to be 'Cargo', got %s", name)
}
}
func TestCargoManager_InstallManager(t *testing.T) {
cm := &CargoManager{}
// Mock exec.LookPath
shell.ExecLookPath = func(file string) (string, error) {
if file == "cargo" {
return "/usr/bin/cargo", nil
}
return "", exec.ErrNotFound
}
defer func() { shell.ExecLookPath = exec.LookPath }()
if err := cm.InstallManager(); err != nil {
t.Errorf("Expected no error, got %v", err)
}
}
func TestCargoManager_InstallPackage(t *testing.T) {
cm := &CargoManager{}
// Mock exec.Command
shell.ExecCommand = func(name string, arg ...string) *exec.Cmd {
return exec.Command("echo", "mocked cargo install")
}
defer func() { shell.ExecCommand = exec.Command }()
if err := cm.InstallPackage("test-package"); err != nil {
t.Errorf("Expected no error, got %v", err)
}
}

118
packagemanager/flatpak.go Normal file
View file

@ -0,0 +1,118 @@
package packagemanager
import (
"fmt"
"log"
"strings"
"system_setup_tool/internal/shell"
)
type FlatpakManager struct {
OS *OS
SudoPassword string
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)
if err != nil {
return err
}
return nil
}
func (f *FlatpakManager) Name() string {
return "Flatpak"
}
func (f *FlatpakManager) InstallManager() error {
if _, err := shell.ExecLookPath("flatpak"); err == nil {
return nil
}
err := installFlatpak(f.OS, f.SudoPassword)
if err != nil {
return err
}
err = addFlatpakRemotes(f.Config.Remotes)
if err != nil {
return err
}
return nil
}
type FlatpakConfig struct {
Enable bool `mapstructure:"enable"`
Remotes []Remote `mapstructure:"remotes"`
Packages []string `mapstructure:"packages"`
}
type Remote struct {
Name string `mapstructure:"name"`
URL string `mapstructure:"url"`
}
func installFlatpak(os *OS, sudoPassword string) error {
var command string
switch os.PackageManager {
case "pacman":
command = "pacman -S --noconfirm --needed flatpak"
case "apt":
command = "apt install -y flatpak"
case "dnf":
command = "dnf install -y flatpak"
default:
return fmt.Errorf("keine Flatpak-Installation für OS %s definiert", os.ID)
}
if err := InstallPackage(command, "", sudoPassword); err != nil {
return fmt.Errorf("fehler bei der Flatpak-Installation: %v", err)
}
return nil
}
func addFlatpakRemotes(remotes []Remote) error {
for _, remote := range remotes {
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)
}
}
return nil
}
func (f *FlatpakManager) InstallPackage(pkg string) error {
cmd := shell.ExecCommand("flatpak", "install", "-y", pkg)
return cmd.Run()
}
func (f *FlatpakManager) RemovePackage(pkg string) error {
cmd := shell.ExecCommand("flatpak", "uninstall", "-y", pkg)
return cmd.Run()
}
func (f *FlatpakManager) SearchPackage(pkg string) []string {
cmd := shell.ExecCommand("flatpak", "search", pkg)
packages, err := cmd.Output()
if err != nil {
log.Printf("error fetching %s packages: %v", f.Name(), err)
}
packageList := strings.Split(strings.TrimSpace(string(packages)), "\n")
return packageList
}

View file

@ -0,0 +1,44 @@
package packagemanager
import (
"os/exec"
"system_setup_tool/internal/shell"
"testing"
)
func TestFlatpakManager_Name(t *testing.T) {
fm := &FlatpakManager{}
if name := fm.Name(); name != "Flatpak" {
t.Errorf("Expected name to be 'Flatpak', got %s", name)
}
}
func TestFlatpakManager_InstallManager(t *testing.T) {
fm := &FlatpakManager{
OS: &OS{PackageManager: "apt"},
SudoPassword: "testpass",
Config: FlatpakConfig{
Remotes: []Remote{{Name: "test", URL: "http://test.com"}},
},
}
// Mock exec.LookPath and other functions as needed
if err := fm.InstallManager(); err != nil {
t.Errorf("Expected no error, got %v", err)
}
}
func TestFlatpakManager_InstallPackage(t *testing.T) {
fm := &FlatpakManager{}
// Mock exec.Command
shell.ExecCommand = func(name string, arg ...string) *exec.Cmd {
return exec.Command("echo", "mocked flatpak install")
}
defer func() { shell.ExecCommand = exec.Command }()
if err := fm.InstallPackage("test-package"); err != nil {
t.Errorf("Expected no error, got %v", err)
}
}

47
packagemanager/golang.go Normal file
View file

@ -0,0 +1,47 @@
package packagemanager
import "system_setup_tool/internal/shell"
type GolangManager struct{}
func (g *GolangManager) Install(packages []string) error {
if len(packages) == 0 {
return nil
}
err := InstallWithProgress(g, packages)
if err != nil {
return err
}
return nil
}
func (g *GolangManager) Name() string {
return "Golang"
}
func (g *GolangManager) InstallManager() error {
if _, err := shell.ExecLookPath("brew"); err != nil {
installHomebrew()
}
if _, err := shell.ExecLookPath("go"); err == nil {
return nil
}
cmd := shell.ExecCommand("brew", "install", "golang")
return cmd.Run()
}
func (g *GolangManager) InstallPackage(pkg string) error {
cmd := shell.ExecCommand("go", "install", pkg+"@latest")
return cmd.Run()
}
func (g *GolangManager) RemovePackage(pkg string) error {
cmd := shell.ExecCommand("go", "uninstall", pkg)
return cmd.Run()
}
func (g *GolangManager) SearchPackage(pkg string) []string {
return []string{}
}

View file

@ -0,0 +1,45 @@
package packagemanager
import (
"os/exec"
"system_setup_tool/internal/shell"
"testing"
)
func TestGolangManager_Name(t *testing.T) {
gm := &GolangManager{}
if name := gm.Name(); name != "Golang" {
t.Errorf("Expected name to be 'Golang', got %s", name)
}
}
func TestGolangManager_InstallManager(t *testing.T) {
gm := &GolangManager{}
// Mock exec.LookPath
shell.ExecLookPath = func(file string) (string, error) {
if file == "go" {
return "/usr/local/go/bin/go", nil
}
return "", exec.ErrNotFound
}
defer func() { shell.ExecLookPath = exec.LookPath }()
if err := gm.InstallManager(); err != nil {
t.Errorf("Expected no error, got %v", err)
}
}
func TestGolangManager_InstallPackage(t *testing.T) {
gm := &GolangManager{}
// Mock exec.Command
shell.ExecCommand = func(name string, arg ...string) *exec.Cmd {
return exec.Command("echo", "mocked go install")
}
defer func() { shell.ExecCommand = exec.Command }()
if err := gm.InstallPackage("github.com/test/package"); err != nil {
t.Errorf("Expected no error, got %v", err)
}
}

View file

@ -0,0 +1,61 @@
package packagemanager
import (
"fmt"
"log"
"strings"
"system_setup_tool/internal/shell"
)
type HomebrewManager struct{}
func (h *HomebrewManager) Install(packages []string) error {
if len(packages) == 0 {
return nil
}
err := InstallWithProgress(h, packages)
if err != nil {
return err
}
return nil
}
func (h *HomebrewManager) Name() string {
return "Homebrew"
}
func (h *HomebrewManager) InstallManager() error {
if _, err := shell.ExecLookPath("brew"); err == nil {
return nil
}
return installHomebrew()
}
func installHomebrew() error {
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)
}
return nil
}
func (h *HomebrewManager) InstallPackage(pkg string) error {
cmd := shell.ExecCommand("brew", "install", pkg)
return cmd.Run()
}
func (h *HomebrewManager) RemovePackage(pkg string) error {
cmd := shell.ExecCommand("brew", "uninstall", pkg)
return cmd.Run()
}
func (h *HomebrewManager) SearchPackage(pkg string) []string {
cmd := shell.ExecCommand("brew", "search", pkg)
packages, err := cmd.Output()
if err != nil {
log.Printf("error fetching %s packages: %v", h.Name(), err)
}
packageList := strings.Split(strings.TrimSpace(string(packages)), "\n")
return packageList
}

View file

@ -0,0 +1,45 @@
package packagemanager
import (
"os/exec"
"system_setup_tool/internal/shell"
"testing"
)
func TestHomebrewManager_Name(t *testing.T) {
hm := &HomebrewManager{}
if name := hm.Name(); name != "Homebrew" {
t.Errorf("Expected name to be 'Homebrew', got %s", name)
}
}
func TestHomebrewManager_InstallManager(t *testing.T) {
hm := &HomebrewManager{}
// Mock exec.LookPath and executeShellCommand
shell.ExecLookPath = func(file string) (string, error) {
if file == "brew" {
return "/usr/local/bin/brew", nil
}
return "", exec.ErrNotFound
}
defer func() { shell.ExecLookPath = exec.LookPath }()
if err := hm.InstallManager(); err != nil {
t.Errorf("Expected no error, got %v", err)
}
}
func TestHomebrewManager_InstallPackage(t *testing.T) {
hm := &HomebrewManager{}
// Mock exec.Command
shell.ExecCommand = func(name string, arg ...string) *exec.Cmd {
return exec.Command("echo", "mocked brew install")
}
defer func() { shell.ExecCommand = exec.Command }()
if err := hm.InstallPackage("test-package"); err != nil {
t.Errorf("Expected no error, got %v", err)
}
}

230
packagemanager/os.go Normal file
View file

@ -0,0 +1,230 @@
package packagemanager
import (
"fmt"
"log"
"os"
"strings"
"system_setup_tool/internal/shell"
)
type OSManager struct {
OS *OS
SudoPassword string
}
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,
}
}
func (o *OSManager) Name() string {
return "OS Package Manager"
}
func (o *OSManager) InstallManager() error { return nil }
func (o *OSManager) Install(packages []string) error {
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)
}
func (o *OSManager) InstallBuildEssentials() error {
return InstallBuildEssentials(o.OS, o.SudoPassword)
}
func (o *OSManager) RemovePackage(pkg string) error {
fullCmd := fmt.Sprintf("%s %s", o.OS.RemoveCommand, pkg)
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 := 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)
return []string{}
}
packageList := strings.Split(strings.TrimSpace(string(packages)), "\n")
return packageList
}
type OS struct {
ID string
Name string
Version string
PackageManager string
InstallCommand string
SearchCommand string
RemoveCommand string
}
func parseOsRelease(osRelease string) *OS {
var result OS
result.ID = "Unknown"
result.Name = "Unknown"
result.Version = "Unknown"
result.PackageManager = "Unkown"
result.InstallCommand = "Unkown"
lines := strings.Split(osRelease, "\n")
for _, line := range lines {
splitLine := strings.SplitN(line, "=", 2)
if len(splitLine) != 2 {
continue
}
switch splitLine[0] {
case "ID":
result.ID = strings.ToLower(strings.Trim(splitLine[1], "\""))
case "NAME":
result.Name = strings.Trim(splitLine[1], "\"")
case "VERSION_ID":
result.Version = strings.Trim(splitLine[1], "\"")
}
}
err := result.getPackageManager()
if err != nil {
log.Fatal(err)
}
err = result.getInstallCommand()
if err != nil {
log.Fatal(err)
}
err = result.getSearchCommand()
if err != nil {
log.Fatal(err)
}
return &result
}
func GetLinuxDistribution() (*OS, error) {
_, err := os.Stat("/etc/os-release")
if os.IsNotExist(err) {
return nil, fmt.Errorf("unable to read system information")
}
osRelease, _ := os.ReadFile("/etc/os-release")
return parseOsRelease(string(osRelease)), nil
}
func (os *OS) getPackageManager() error {
switch os.ID {
case "debian", "ubuntu":
os.PackageManager = "apt"
return nil
case "arch":
os.PackageManager = "pacman"
return nil
case "fedora":
os.PackageManager = "dnf"
return nil
default:
pmcommands := []string{
"apt",
"dnf",
"pacman",
}
for _, pmname := range pmcommands {
_, err := shell.ExecLookPath(pmname)
if err == nil {
os.PackageManager = pmname
return nil
}
}
return fmt.Errorf("no packagemanager found for os: %s", os)
}
}
func (os *OS) getInstallCommand() error {
switch os.PackageManager {
case "apt":
os.InstallCommand = "apt install -y"
return nil
case "pacman":
os.InstallCommand = "pacman -S --noconfirm --needed"
return nil
case "dnf":
os.InstallCommand = "dnf install -y --best"
return nil
default:
return fmt.Errorf("no install command found for package manager: %s", os.ID)
}
}
func (os *OS) getSearchCommand() error {
switch os.PackageManager {
case "apt":
os.SearchCommand = "apt search"
return nil
case "pacman":
os.SearchCommand = "pacman -Ss"
return nil
case "dnf":
os.SearchCommand = "dnf search"
return nil
default:
return fmt.Errorf("no install command found for package manager: %s", os.ID)
}
}
func (os *OS) getDeleteCommand() error {
switch os.PackageManager {
case "apt":
os.RemoveCommand = "apt remove"
return nil
case "pacman":
os.RemoveCommand = "pacman -R"
return nil
case "dnf":
os.RemoveCommand = "dnf remove"
return nil
default:
return fmt.Errorf("no install command found for package manager: %s", os.ID)
}
}
func InstallBuildEssentials(os *OS, sudoPassword string) error {
var command string
switch os.PackageManager {
case "pacman":
command = "pacman -S --noconfirm --needed base-devel"
case "apt":
command = "apt install -y build-essential"
case "dnf":
command = "dnf install -y @development-tools"
default:
return fmt.Errorf("keine Build Essentials für OS %s definiert", os.ID)
}
fmt.Printf("Installiere Build Essentials für %s...\n", os.Name)
if err := InstallPackage(command, "", sudoPassword); err != nil {
return fmt.Errorf("error installing Build Essentials: %v", err)
}
return nil
}

53
packagemanager/os_test.go Normal file
View file

@ -0,0 +1,53 @@
package packagemanager
import (
"testing"
)
func TestNewOSManager(t *testing.T) {
sudoPassword := "testpassword"
manager := NewOSManager(sudoPassword)
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)
}
}
func TestOSManagerName(t *testing.T) {
manager := &OSManager{}
if manager.Name() != "OS Package Manager" {
t.Errorf("Expected name to be 'OS Package Manager', got %s", manager.Name())
}
}
func TestParseOsRelease(t *testing.T) {
osReleaseContent := `
ID=ubuntu
NAME="Ubuntu"
VERSION_ID="20.04"
`
os := parseOsRelease(osReleaseContent)
if os.ID != "ubuntu" {
t.Errorf("Expected ID to be 'ubuntu', got %s", os.ID)
}
if os.Name != "Ubuntu" {
t.Errorf("Expected Name to be 'Ubuntu', got %s", os.Name)
}
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)
}
}

28
packagemanager/package.go Normal file
View file

@ -0,0 +1,28 @@
package packagemanager
import (
"fmt"
"log"
"strings"
"system_setup_tool/internal/shell"
)
type Packages struct {
Headless []string `mapstructure:"headless"`
NonHeadless []string `mapstructure:"non_headless"`
}
func InstallPackage(cmd, pkg, sudoPassword string) error {
fullCmd := fmt.Sprintf("%s %s", cmd, pkg)
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") {
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
}

View file

@ -0,0 +1,10 @@
package packagemanager
type PackageManager interface {
Install(packages []string) error
InstallPackage(pkg string) error
InstallManager() error
RemovePackage(pkg string) error
SearchPackage(pkg string) []string
Name() string
}

48
packagemanager/pipx.go Normal file
View file

@ -0,0 +1,48 @@
package packagemanager
import "system_setup_tool/internal/shell"
type PipxManager struct{}
func (p *PipxManager) Install(packages []string) error {
if len(packages) == 0 {
return nil
}
err := InstallWithProgress(p, packages)
if err != nil {
return err
}
return nil
}
func (p *PipxManager) Name() string {
return "Pipx"
}
func (p *PipxManager) InstallManager() error {
if _, err := shell.ExecLookPath("brew"); err != nil {
installHomebrew()
}
if _, err := shell.ExecLookPath("pipx"); err == nil {
return nil
}
cmd := shell.ExecCommand("brew", "install", "pipx")
return cmd.Run()
}
func (p *PipxManager) InstallPackage(pkg string) error {
cmd := shell.ExecCommand("pipx", "install", pkg)
return cmd.Run()
}
func (p *PipxManager) RemovePackage(pkg string) error {
cmd := shell.ExecCommand("pipx", "uninstall", pkg)
return cmd.Run()
}
func (p *PipxManager) SearchPackage(pkg string) []string {
return []string{}
}

View file

@ -0,0 +1,45 @@
package packagemanager
import (
"os/exec"
"system_setup_tool/internal/shell"
"testing"
)
func TestPipxManager_Name(t *testing.T) {
pm := &PipxManager{}
if name := pm.Name(); name != "Pipx" {
t.Errorf("Expected name to be 'Pipx', got %s", name)
}
}
func TestPipxManager_InstallManager(t *testing.T) {
pm := &PipxManager{}
// Mock exec.LookPath
shell.ExecLookPath = func(file string) (string, error) {
if file == "pipx" {
return "/usr/bin/pipx", nil
}
return "", exec.ErrNotFound
}
defer func() { shell.ExecLookPath = exec.LookPath }()
if err := pm.InstallManager(); err != nil {
t.Errorf("Expected no error, got %v", err)
}
}
func TestPipxManager_InstallPackage(t *testing.T) {
pm := &PipxManager{}
// Mock exec.Command
shell.ExecCommand = func(name string, arg ...string) *exec.Cmd {
return exec.Command("echo", "mocked pipx install")
}
defer func() { shell.ExecCommand = exec.Command }()
if err := pm.InstallPackage("test-package"); err != nil {
t.Errorf("Expected no error, got %v", err)
}
}

19
packagemanager/utils.go Normal file
View file

@ -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
}