system_setup_tool/packagemanager/os.go
Patryk Hegenberg 8281883e4d feat(cli,packagemanager): implemented first working windows and macos versions
in order to implement sst for windows and macos the structure of
osmanager has been changed.
Now osmanager has a new field pm, which contains the specific
packagemanager for the os.
Linux packagemanagers have been implemented as packagemanagers on their
own.
2025-01-20 21:28:44 +01:00

232 lines
5.1 KiB
Go

package packagemanager
import (
"fmt"
"log"
"os"
"runtime"
"strings"
"system_setup_tool/internal/shell"
)
type OSManager struct {
OS *OS
SudoPassword string
pm PackageManager
}
func NewOSManager(sudoPassword string) *OSManager {
switch runtime.GOOS {
case "linux":
os, err := GetLinuxDistribution()
if err != nil {
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")
}
return nil
}
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.pm, packages)
if err != nil {
return err
}
return nil
}
func (o *OSManager) InstallPackage(pkg string) error {
return o.pm.InstallPackage(pkg) //(o.OS.InstallCommand, pkg, o.SudoPassword)
}
func (o *OSManager) InstallBuildEssentials() error {
return InstallBuildEssentials(o.OS, o.SudoPassword)
}
func (o *OSManager) RemovePackage(pkg string) error {
return o.pm.RemovePackage(pkg)
}
func (o *OSManager) SearchPackage(pkg string) []string {
return o.pm.SearchPackage(pkg)
}
func (o *OSManager) UpdatePackage(pkg string) error {
return o.pm.UpdatePackage(pkg)
}
func (o *OSManager) UpdateAllPackages() error {
return o.pm.UpdateAllPackages()
}
type OS struct {
ID string
Name string
Version string
PackageManager string
}
func parseOsRelease(osRelease string) *OS {
var result OS
result.ID = "Unknown"
result.Name = "Unknown"
result.Version = "Unknown"
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], "\"")
}
}
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 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
}
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)
}
}