diff --git a/cmd/install_cmd.go b/cmd/install_cmd.go index b31c3b2..be9de12 100644 --- a/cmd/install_cmd.go +++ b/cmd/install_cmd.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "log" + "runtime" "system_setup_tool/utils" pm "system_setup_tool/packagemanager" @@ -24,14 +25,15 @@ var installCmd = &cobra.Command{ managerName = "homebrew" } switch managerName { - case "os": - sudoPassword, err := utils.GetSudoPassword() - if err != nil { - log.Fatal(err) - } - osManager := pm.NewOSManager(sudoPassword) - if err := osManager.Install([]string{packageName}); err != nil { - log.Printf("error: %v\n", err) + case "OS Package Manager": + if runtime.GOOS != "windows" { + sudoPassword, err := utils.GetSudoPassword() + if err != nil { + log.Fatal(err) + } + manager = pm.NewOSManager(sudoPassword) + } else { + manager = pm.NewOSManager("") } case "homebrew": manager = &pm.HomebrewManager{} @@ -45,7 +47,7 @@ var installCmd = &cobra.Command{ fmt.Println("No PackageManager found") return } - if err := manager.InstallPackage(packageName); err != nil { + if err := manager.Install([]string{packageName}); err != nil { log.Printf("error: %v\n", err) } }, diff --git a/cmd/remove_cmd.go b/cmd/remove_cmd.go index a41ff61..30b80f2 100644 --- a/cmd/remove_cmd.go +++ b/cmd/remove_cmd.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "log" + "runtime" "system_setup_tool/utils" pm "system_setup_tool/packagemanager" @@ -24,14 +25,15 @@ var removeCmd = &cobra.Command{ managerName = "homebrew" } switch managerName { - case "os": - sudoPassword, err := utils.GetSudoPassword() - if err != nil { - log.Fatal(err) - } - osManager := pm.NewOSManager(sudoPassword) - if err := osManager.Install([]string{packageName}); err != nil { - log.Printf("error: %v\n", err) + case "OS Package Manager": + if runtime.GOOS != "windows" { + sudoPassword, err := utils.GetSudoPassword() + if err != nil { + log.Fatal(err) + } + manager = pm.NewOSManager(sudoPassword) + } else { + manager = pm.NewOSManager("") } case "homebrew": manager = &pm.HomebrewManager{} diff --git a/cmd/search_cmd.go b/cmd/search_cmd.go index 969f08c..6fe1f68 100644 --- a/cmd/search_cmd.go +++ b/cmd/search_cmd.go @@ -2,6 +2,8 @@ package cmd import ( "fmt" + "log" + "runtime" "strings" pm "system_setup_tool/packagemanager" @@ -22,10 +24,26 @@ var searchCmd = &cobra.Command{ managerName = "homebrew" } - managers := []pm.PackageManager{ - pm.NewOSManager(""), - &pm.HomebrewManager{}, - &pm.FlatpakManager{}, + var managers []pm.PackageManager + switch runtime.GOOS { + case "linux": + managers = []pm.PackageManager{ + pm.NewOSManager(""), + &pm.HomebrewManager{}, + &pm.FlatpakManager{}, + } + case "windows": + managers = []pm.PackageManager{ + pm.NewOSManager(""), + &pm.WingetManager{}, + &pm.ChocoManager{}, + } + case "darwin": + managers = []pm.PackageManager{ + pm.NewOSManager(""), + } + default: + log.Println("No Package Managers found") } if managerName != "" { diff --git a/cmd/update_command.go b/cmd/update_command.go index ebb23b2..a6162b5 100644 --- a/cmd/update_command.go +++ b/cmd/update_command.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "log" + "runtime" pm "system_setup_tool/packagemanager" "system_setup_tool/utils" @@ -25,14 +26,15 @@ var updateCmd = &cobra.Command{ } switch managerName { - case "os": - sudoPassword, err := utils.GetSudoPassword() - if err != nil { - log.Fatal(err) - } - osManager := pm.NewOSManager(sudoPassword) - if err := osManager.Install([]string{packageName}); err != nil { - log.Printf("error: %v\n", err) + case "OS Package Manager": + if runtime.GOOS != "windows" { + sudoPassword, err := utils.GetSudoPassword() + if err != nil { + log.Fatal(err) + } + manager = pm.NewOSManager(sudoPassword) + } else { + manager = pm.NewOSManager("") } case "homebrew": manager = &pm.HomebrewManager{} diff --git a/go.mod b/go.mod index 72088a7..396303f 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/schollz/progressbar/v3 v3.18.0 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 + golang.org/x/sys v0.29.0 ) require ( @@ -50,7 +51,6 @@ require ( go.uber.org/multierr v1.9.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/sync v0.9.0 // indirect - golang.org/x/sys v0.29.0 // indirect golang.org/x/term v0.28.0 // indirect golang.org/x/text v0.18.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/packagemanager/apt.go b/packagemanager/apt.go new file mode 100644 index 0000000..dd63647 --- /dev/null +++ b/packagemanager/apt.go @@ -0,0 +1,76 @@ +package packagemanager + +import ( + "fmt" + "log" + "strings" + "system_setup_tool/internal/shell" +) + +type AptManager struct { + SudoPassword string +} + +func (a *AptManager) Install(packages []string) error { + if len(packages) == 0 { + return nil + } + err := InstallWithProgress(a, packages) + if err != nil { + return err + } + return nil +} + +func (a *AptManager) Name() string { + return "OS Home Manager" +} + +func (a *AptManager) InstallManager() error { + return nil +} + +func (a *AptManager) InstallPackage(pkg string) error { + cmd := "apt install -y" + fullCmd := fmt.Sprintf("%s %s", cmd, pkg) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(a.SudoPassword + "\n") + return command.Run() +} + +func (a *AptManager) RemovePackage(pkg string) error { + cmd := "apt remove -y" + fullCmd := fmt.Sprintf("%s %s", cmd, pkg) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(a.SudoPassword + "\n") + return command.Run() +} + +func (a *AptManager) SearchPackage(pkg string) []string { + cmd := "apt search -y" + fullCmd := fmt.Sprintf("%s %s", cmd, pkg) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(a.SudoPassword + "\n") + packages, err := command.Output() + if err != nil { + log.Printf("error fetching %s packages: %v", a.Name(), err) + } + packageList := strings.Split(strings.TrimSpace(string(packages)), "\n") + return packageList +} + +func (a *AptManager) UpdatePackage(pkg string) error { + cmd := "apt update -y" + fullCmd := fmt.Sprintf("%s %s", cmd, pkg) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(a.SudoPassword + "\n") + return command.Run() +} + +func (a *AptManager) UpdateAllPackages() error { + cmd := "apt update -y" + fullCmd := fmt.Sprintf("%s", cmd) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(a.SudoPassword + "\n") + return command.Run() +} diff --git a/packagemanager/dnf.go b/packagemanager/dnf.go new file mode 100644 index 0000000..d68a59f --- /dev/null +++ b/packagemanager/dnf.go @@ -0,0 +1,76 @@ +package packagemanager + +import ( + "fmt" + "log" + "strings" + "system_setup_tool/internal/shell" +) + +type DnfManager struct { + SudoPassword string +} + +func (d *DnfManager) Install(packages []string) error { + if len(packages) == 0 { + return nil + } + err := InstallWithProgress(d, packages) + if err != nil { + return err + } + return nil +} + +func (d *DnfManager) Name() string { + return "OS Home Manager" +} + +func (d *DnfManager) InstallManager() error { + return nil +} + +func (d *DnfManager) InstallPackage(pkg string) error { + cmd := "dnf install -y --best" + fullCmd := fmt.Sprintf("%s %s", cmd, pkg) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(d.SudoPassword + "\n") + return command.Run() +} + +func (d *DnfManager) RemovePackage(pkg string) error { + cmd := "dnf remove -y" + fullCmd := fmt.Sprintf("%s %s", cmd, pkg) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(d.SudoPassword + "\n") + return command.Run() +} + +func (d *DnfManager) SearchPackage(pkg string) []string { + cmd := "dnf search -y" + fullCmd := fmt.Sprintf("%s %s", cmd, pkg) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(d.SudoPassword + "\n") + packages, err := command.Output() + if err != nil { + log.Printf("error fetching %s packages: %v", d.Name(), err) + } + packageList := strings.Split(strings.TrimSpace(string(packages)), "\n") + return packageList +} + +func (d *DnfManager) UpdatePackage(pkg string) error { + cmd := "dnf update -y" + fullCmd := fmt.Sprintf("%s %s", cmd, pkg) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(d.SudoPassword + "\n") + return command.Run() +} + +func (d *DnfManager) UpdateAllPackages() error { + cmd := "dnf update -y" + fullCmd := fmt.Sprintf("%s", cmd) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(d.SudoPassword + "\n") + return command.Run() +} diff --git a/packagemanager/os.go b/packagemanager/os.go index e6fe90a..efc84d6 100644 --- a/packagemanager/os.go +++ b/packagemanager/os.go @@ -12,6 +12,7 @@ import ( type OSManager struct { OS *OS SudoPassword string + pm PackageManager } func NewOSManager(sudoPassword string) *OSManager { @@ -19,14 +20,66 @@ func NewOSManager(sudoPassword string) *OSManager { case "linux": os, err := GetLinuxDistribution() if err != nil { - log.Fatalf("error geting os information: %v", err) + log.Fatalf("error getting os information: %v", err) + } + var pm PackageManager + switch os.ID { + case "debian", "ubuntu": + pm = &AptManager{SudoPassword: sudoPassword} + case "arch": + pm = &PacmanManager{SudoPassword: sudoPassword} + case "fedora": + pm = &DnfManager{SudoPassword: sudoPassword} + default: + log.Fatalf("Unsupported Linux distribution: %s", os.ID) } return &OSManager{ OS: os, SudoPassword: sudoPassword, + pm: pm, } case "windows": + os, err := getPlatformInfo() + if err != nil { + return nil + } + + if _, err := shell.ExecLookPath("winget"); err == nil { + return &OSManager{ + OS: os, + SudoPassword: "", + pm: &WingetManager{}, + } + } else if _, err := shell.ExecLookPath("choco"); err == nil { + return &OSManager{ + OS: os, + SudoPassword: "", + pm: &ChocoManager{}, + } + } else { + log.Println("Neither Winget nor Chocolatey found on Windows. Package management functionality will be limited.") + } case "darwin": + os, err := platformInfoMac() + if err != nil { + return nil + } + _, err = shell.ExecLookPath("brew") + if err == nil { + return &OSManager{ + OS: os, + SudoPassword: sudoPassword, + pm: &HomebrewManager{}, + } + } else { + manager := &HomebrewManager{} + manager.InstallManager() + return &OSManager{ + OS: os, + SudoPassword: sudoPassword, + pm: manager, + } + } default: log.Fatal("Operating system not supported") } @@ -43,7 +96,7 @@ func (o *OSManager) Install(packages []string) error { if len(packages) == 0 { return nil } - err := InstallWithProgress(o, packages) + err := InstallWithProgress(o.pm, packages) if err != nil { return err } @@ -51,7 +104,7 @@ func (o *OSManager) Install(packages []string) error { } func (o *OSManager) InstallPackage(pkg string) error { - return InstallPackage(o.OS.InstallCommand, pkg, o.SudoPassword) + return o.pm.InstallPackage(pkg) //(o.OS.InstallCommand, pkg, o.SudoPassword) } func (o *OSManager) InstallBuildEssentials() error { @@ -59,42 +112,19 @@ func (o *OSManager) InstallBuildEssentials() error { } 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() + return o.pm.RemovePackage(pkg) } 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 + return o.pm.SearchPackage(pkg) } func (o *OSManager) UpdatePackage(pkg string) error { - fullCmd := fmt.Sprintf("%s %s", o.OS.UpdateCommand, pkg) - command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) - command.Stdin = strings.NewReader(o.SudoPassword + "\n") - return command.Run() + return o.pm.UpdatePackage(pkg) } func (o *OSManager) UpdateAllPackages() error { - fullCmd := fmt.Sprintf("%s", o.OS.UpdateCommand) - command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) - command.Stdin = strings.NewReader(o.SudoPassword + "\n") - return command.Run() + return o.pm.UpdateAllPackages() } type OS struct { @@ -102,10 +132,6 @@ type OS struct { Name string Version string PackageManager string - InstallCommand string - SearchCommand string - RemoveCommand string - UpdateCommand string } func parseOsRelease(osRelease string) *OS { @@ -113,11 +139,6 @@ func parseOsRelease(osRelease string) *OS { result.ID = "Unknown" result.Name = "Unknown" result.Version = "Unknown" - result.PackageManager = "Unkown" - result.InstallCommand = "Unkown" - result.SearchCommand = "Unkown" - result.RemoveCommand = "Unkown" - result.UpdateCommand = "Unkown" lines := strings.Split(osRelease, "\n") @@ -135,22 +156,6 @@ func parseOsRelease(osRelease string) *OS { 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) - } - err = result.getUpdateCommand() - if err != nil { - log.Fatal(err) - } return &result } @@ -164,98 +169,6 @@ func GetLinuxDistribution() (*OS, error) { 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 (os *OS) getUpdateCommand() error { - switch os.PackageManager { - case "apt": - os.UpdateCommand = "apt update" - return nil - case "pacman": - os.UpdateCommand = "pacman -S" - return nil - case "dnf": - os.UpdateCommand = "dnf update" - 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 { @@ -275,3 +188,45 @@ func InstallBuildEssentials(os *OS, sudoPassword string) error { } return nil } + +func getSysctlValue(key string) (string, error) { + stdout, err := shell.ExecCommand(".", "sysctl", key).Output() + if err != nil { + return "", err + } + version := strings.TrimPrefix(string(stdout), key+": ") + return strings.TrimSpace(version), nil +} + +func platformInfoMac() (*OS, error) { + var result OS + result.ID = "Unknown" + result.Name = "MacOS" + result.Version = "Unknown" + + version, err := getSysctlValue("kern.osproductversion") + if err != nil { + return nil, err + } + result.Version = version + ID, err := getSysctlValue("kern.osversion") + if err != nil { + return nil, err + } + result.ID = ID + + return &result, nil +} + +func getPlatformInfo() (*OS, error) { + switch runtime.GOOS { + case "windows": + return platformInfoWin() + case "darwin": + return platformInfoMac() + case "linux": + return GetLinuxDistribution() + default: + return nil, fmt.Errorf("unsupported operating system: %s", runtime.GOOS) + } +} diff --git a/packagemanager/os_test.go b/packagemanager/os_test.go index 25dbf58..f4d76eb 100644 --- a/packagemanager/os_test.go +++ b/packagemanager/os_test.go @@ -41,13 +41,4 @@ 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/packagemanager/pacman.go b/packagemanager/pacman.go new file mode 100644 index 0000000..94f018b --- /dev/null +++ b/packagemanager/pacman.go @@ -0,0 +1,76 @@ +package packagemanager + +import ( + "fmt" + "log" + "strings" + "system_setup_tool/internal/shell" +) + +type PacmanManager struct { + SudoPassword string +} + +func (m *PacmanManager) Install(packages []string) error { + if len(packages) == 0 { + return nil + } + err := InstallWithProgress(m, packages) + if err != nil { + return err + } + return nil +} + +func (m *PacmanManager) Name() string { + return "OS Home Manager" +} + +func (m *PacmanManager) InstallManager() error { + return nil +} + +func (m *PacmanManager) InstallPackage(pkg string) error { + cmd := "pacman -S --noconfirm --needed" + fullCmd := fmt.Sprintf("%s %s", cmd, pkg) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(m.SudoPassword + "\n") + return command.Run() +} + +func (m *PacmanManager) RemovePackage(pkg string) error { + cmd := "pacman -R" + fullCmd := fmt.Sprintf("%s %s", cmd, pkg) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(m.SudoPassword + "\n") + return command.Run() +} + +func (p *PacmanManager) SearchPackage(pkg string) []string { + cmd := "pacman -Ss" + fullCmd := fmt.Sprintf("%s %s", cmd, pkg) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(p.SudoPassword + "\n") + packages, err := command.Output() + if err != nil { + log.Printf("error fetching %s packages: %v", p.Name(), err) + } + packageList := strings.Split(strings.TrimSpace(string(packages)), "\n") + return packageList +} + +func (p *PacmanManager) UpdatePackage(pkg string) error { + cmd := "pacman -S" + fullCmd := fmt.Sprintf("%s %s", cmd, pkg) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(p.SudoPassword + "\n") + return command.Run() +} + +func (p *PacmanManager) UpdateAllPackages() error { + cmd := "apt -S" + fullCmd := fmt.Sprintf("%s", cmd) + command := shell.ExecCommand("sudo", "-S", "sh", "-c", fullCmd) + command.Stdin = strings.NewReader(p.SudoPassword + "\n") + return command.Run() +} diff --git a/packagemanager/win.go b/packagemanager/win.go new file mode 100644 index 0000000..4488b57 --- /dev/null +++ b/packagemanager/win.go @@ -0,0 +1,30 @@ +//go:build windows +// +build windows + +package packagemanager + +import ( + "fmt" + + "golang.org/x/sys/windows/registry" +) + +func platformInfoWin() (*OS, error) { + var result OS + result.ID = "Unknown" + result.Name = "Windows" + result.Version = "Unknown" + + key, _ := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) + + productName, _, _ := key.GetStringValue("ProductName") + currentBuild, _, _ := key.GetStringValue("CurrentBuildNumber") + displayVersion, _, _ := key.GetStringValue("DisplayVersion") + releaseId, _, _ := key.GetStringValue("ReleaseId") + + result.Name = productName + result.Version = fmt.Sprintf("%s (Build: %s)", releaseId, currentBuild) + result.ID = displayVersion + + return &result, key.Close() +} diff --git a/packagemanager/win_dummy.go b/packagemanager/win_dummy.go new file mode 100644 index 0000000..3482159 --- /dev/null +++ b/packagemanager/win_dummy.go @@ -0,0 +1,8 @@ +//go:build linux +// +build linux + +package packagemanager + +func platformInfoWin() (*OS, error) { + return nil, nil +}