From a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5 Mon Sep 17 00:00:00 2001 From: Nippy Date: Sat, 9 May 2026 13:33:09 +0200 Subject: raveos update --- .../core/internal/distros/opensuse.go | 856 --------------------- 1 file changed, 856 deletions(-) delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/opensuse.go (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/opensuse.go') diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/opensuse.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/opensuse.go deleted file mode 100644 index 62f40cd..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/opensuse.go +++ /dev/null @@ -1,856 +0,0 @@ -package distros - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "slices" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" -) - -func init() { - Register("opensuse-tumbleweed", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution { - return NewOpenSUSEDistribution(config, logChan) - }) - Register("opensuse-leap", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution { - return NewOpenSUSEDistribution(config, logChan) - }) - Register("opensuse-slowroll", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution { - return NewOpenSUSEDistribution(config, logChan) - }) -} - -type OpenSUSEDistribution struct { - *BaseDistribution - *ManualPackageInstaller - config DistroConfig -} - -const openSUSENiriWaylandServerPackage = "libwayland-server0" - -func NewOpenSUSEDistribution(config DistroConfig, logChan chan<- string) *OpenSUSEDistribution { - base := NewBaseDistribution(logChan) - return &OpenSUSEDistribution{ - BaseDistribution: base, - ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base}, - config: config, - } -} - -func (o *OpenSUSEDistribution) GetID() string { - return o.config.ID -} - -func (o *OpenSUSEDistribution) GetColorHex() string { - return o.config.ColorHex -} - -func (o *OpenSUSEDistribution) GetFamily() DistroFamily { - return o.config.Family -} - -func (o *OpenSUSEDistribution) GetPackageManager() PackageManagerType { - return PackageManagerZypper -} - -func (o *OpenSUSEDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) { - return o.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty) -} - -func (o *OpenSUSEDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) { - var dependencies []deps.Dependency - - // DMS at the top (shell is prominent) - dependencies = append(dependencies, o.detectDMS()) - - // Terminal with choice support - dependencies = append(dependencies, o.detectSpecificTerminal(terminal)) - - // Common detections using base methods - dependencies = append(dependencies, o.detectGit()) - dependencies = append(dependencies, o.detectWindowManager(wm)) - dependencies = append(dependencies, o.detectQuickshell()) - dependencies = append(dependencies, o.detectDMSGreeter()) - dependencies = append(dependencies, o.detectXDGPortal()) - dependencies = append(dependencies, o.detectAccountsService()) - - // Hyprland-specific tools - if wm == deps.WindowManagerHyprland { - dependencies = append(dependencies, o.detectHyprlandTools()...) - } - - // Niri-specific tools - if wm == deps.WindowManagerNiri { - dependencies = append(dependencies, o.detectXwaylandSatellite()) - } - - dependencies = append(dependencies, o.detectMatugen()) - dependencies = append(dependencies, o.detectDgop()) - - return dependencies, nil -} - -func (o *OpenSUSEDistribution) detectXDGPortal() deps.Dependency { - return o.detectPackage("xdg-desktop-portal-gtk", "Desktop integration portal for GTK", o.packageInstalled("xdg-desktop-portal-gtk")) -} - -func (o *OpenSUSEDistribution) packageInstalled(pkg string) bool { - cmd := exec.Command("rpm", "-q", pkg) - err := cmd.Run() - return err == nil -} - -func (o *OpenSUSEDistribution) detectDMSGreeter() deps.Dependency { - return o.detectOptionalPackage("dms-greeter", "DankMaterialShell greetd greeter", o.packageInstalled("dms-greeter")) -} - -func (o *OpenSUSEDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { - return o.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant)) -} - -func (o *OpenSUSEDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping { - packages := map[string]PackageMapping{ - // Standard zypper packages - "git": {Name: "git", Repository: RepoTypeSystem}, - "kitty": {Name: "kitty", Repository: RepoTypeSystem}, - "alacritty": {Name: "alacritty", Repository: RepoTypeSystem}, - "xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem}, - "accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem}, - - // DMS packages from OBS - "dms (DankMaterialShell)": o.getDmsMapping(variants["dms (DankMaterialShell)"]), - "quickshell": o.getQuickshellMapping(variants["quickshell"]), - "dms-greeter": {Name: "dms-greeter", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}, - "ghostty": {Name: "ghostty", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}, - "matugen": {Name: "matugen", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}, - "dgop": {Name: "dgop", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}, - } - - switch wm { - case deps.WindowManagerHyprland: - packages["hyprland"] = PackageMapping{Name: "hyprland", Repository: RepoTypeSystem} - packages["hyprctl"] = PackageMapping{Name: "hyprland", Repository: RepoTypeSystem} - packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem} - case deps.WindowManagerNiri: - // Niri stable has native package support on openSUSE - niriVariant := variants["niri"] - packages["niri"] = o.getNiriMapping(niriVariant) - packages["xwayland-satellite"] = o.getXwaylandSatelliteMapping(niriVariant) - } - - return packages -} - -func (o *OpenSUSEDistribution) getDmsMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "dms-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:dms-git"} - } - return PackageMapping{Name: "dms", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:dms"} -} - -func (o *OpenSUSEDistribution) getQuickshellMapping(variant deps.PackageVariant) PackageMapping { - if forceQuickshellGit || variant == deps.VariantGit { - return PackageMapping{Name: "quickshell-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"} - } - return PackageMapping{Name: "quickshell", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"} -} - -func (o *OpenSUSEDistribution) getNiriMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "niri-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"} - } - return PackageMapping{Name: "niri", Repository: RepoTypeSystem} -} - -func (o *OpenSUSEDistribution) getXwaylandSatelliteMapping(variant deps.PackageVariant) PackageMapping { - if variant == deps.VariantGit { - return PackageMapping{Name: "xwayland-satellite-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"} - } - return PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeSystem} -} - -func (o *OpenSUSEDistribution) detectXwaylandSatellite() deps.Dependency { - status := deps.StatusMissing - if o.commandExists("xwayland-satellite") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "xwayland-satellite", - Status: status, - Description: "Xwayland support", - Required: true, - } -} - -func (o *OpenSUSEDistribution) detectAccountsService() deps.Dependency { - status := deps.StatusMissing - if o.packageInstalled("accountsservice") { - status = deps.StatusInstalled - } - - return deps.Dependency{ - Name: "accountsservice", - Status: status, - Description: "D-Bus interface for user account query and manipulation", - Required: true, - } -} - -func (o *OpenSUSEDistribution) getPrerequisites() []string { - return []string{} -} - -func (o *OpenSUSEDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - prerequisites := o.getPrerequisites() - var missingPkgs []string - - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.06, - Step: "Checking prerequisites...", - IsComplete: false, - LogOutput: "Checking prerequisite packages", - } - - for _, pkg := range prerequisites { - checkCmd := exec.CommandContext(ctx, "rpm", "-q", pkg) - if err := checkCmd.Run(); err != nil { - missingPkgs = append(missingPkgs, pkg) - } - } - - _, err := exec.LookPath("go") - if err != nil { - o.log("go not found in PATH, will install go") - missingPkgs = append(missingPkgs, "go") - } else { - o.log("go already available in PATH") - } - - if len(missingPkgs) == 0 { - o.log("All prerequisites already installed") - return nil - } - - o.log(fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", "))) - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.08, - Step: fmt.Sprintf("Installing %d prerequisites...", len(missingPkgs)), - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo zypper install -y %s", strings.Join(missingPkgs, " ")), - LogOutput: fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", ")), - } - - args := []string{"zypper", "install", "-y"} - args = append(args, missingPkgs...) - cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " ")) - output, err := cmd.CombinedOutput() - if err != nil { - o.logError("failed to install prerequisites", err) - o.log(fmt.Sprintf("Prerequisites command output: %s", string(output))) - return fmt.Errorf("failed to install prerequisites: %w", err) - } - o.log(fmt.Sprintf("Prerequisites install output: %s", string(output))) - - return nil -} - -func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, disabledFlags map[string]bool, skipGlobalUseFlags bool, progressChan chan<- InstallProgressMsg) error { - // Phase 1: Check Prerequisites - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.05, - Step: "Checking system prerequisites...", - IsComplete: false, - LogOutput: "Starting prerequisite check...", - } - - if err := o.disableInstallMediaRepos(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to disable install media repositories: %w", err) - } - - if err := o.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install prerequisites: %w", err) - } - - systemPkgs, obsPkgs, manualPkgs, variantMap := o.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags) - - // Enable OBS repositories - if len(obsPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.15, - Step: "Enabling OBS repositories...", - IsComplete: false, - LogOutput: "Setting up OBS repositories for additional packages", - } - if err := o.enableOBSRepos(ctx, obsPkgs, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to enable OBS repositories: %w", err) - } - } - - // Phase 3: System Packages (Zypper) - if len(systemPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: fmt.Sprintf("Installing %d system packages...", len(systemPkgs)), - IsComplete: false, - NeedsSudo: true, - LogOutput: fmt.Sprintf("Installing system packages: %s", strings.Join(systemPkgs, ", ")), - } - if err := o.installZypperPackages(ctx, systemPkgs, sudoPassword, progressChan, PhaseSystemPackages, "Installing system packages...", 0.40, 0.60); err != nil { - return fmt.Errorf("failed to install zypper packages: %w", err) - } - } - - // OBS Packages - obsPkgNames := o.extractPackageNames(obsPkgs) - if len(obsPkgNames) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseAURPackages, - Progress: 0.65, - Step: fmt.Sprintf("Installing %d OBS packages...", len(obsPkgNames)), - IsComplete: false, - LogOutput: fmt.Sprintf("Installing OBS packages: %s", strings.Join(obsPkgNames, ", ")), - } - if err := o.installZypperPackages(ctx, obsPkgNames, sudoPassword, progressChan, PhaseAURPackages, "Installing OBS packages...", 0.70, 0.85); err != nil { - return fmt.Errorf("failed to install OBS packages: %w", err) - } - } - - // Manual Builds - if len(manualPkgs) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.85, - Step: fmt.Sprintf("Building %d packages from source...", len(manualPkgs)), - IsComplete: false, - LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")), - } - if err := o.InstallManualPackages(ctx, manualPkgs, variantMap, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install manual packages: %w", err) - } - } - - // Configuration - progressChan <- InstallProgressMsg{ - Phase: PhaseConfiguration, - Progress: 0.90, - Step: "Configuring system...", - IsComplete: false, - LogOutput: "Starting post-installation configuration...", - } - - terminal := o.DetectTerminalFromDeps(dependencies) - if err := o.WriteEnvironmentConfig(terminal); err != nil { - o.log(fmt.Sprintf("Warning: failed to write environment config: %v", err)) - } - - if err := o.WriteWindowManagerConfig(wm); err != nil { - o.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err)) - } - - if err := o.EnableDMSService(ctx, wm); err != nil { - o.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err)) - } - - // Complete - progressChan <- InstallProgressMsg{ - Phase: PhaseComplete, - Progress: 1.0, - Step: "Installation complete!", - IsComplete: true, - LogOutput: "All packages installed and configured successfully", - } - - return nil -} - -func (o *OpenSUSEDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []PackageMapping, []string, map[string]deps.PackageVariant) { - systemPkgs := []string{} - obsPkgs := []PackageMapping{} - manualPkgs := []string{} - - variantMap := make(map[string]deps.PackageVariant) - for _, dep := range dependencies { - variantMap[dep.Name] = dep.Variant - } - - packageMap := o.GetPackageMappingWithVariants(wm, variantMap) - - for _, dep := range dependencies { - if disabledFlags[dep.Name] { - continue - } - - if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] { - continue - } - - pkgInfo, exists := packageMap[dep.Name] - if !exists { - o.log(fmt.Sprintf("Warning: No package mapping for %s", dep.Name)) - continue - } - - switch pkgInfo.Repository { - case RepoTypeSystem: - systemPkgs = append(systemPkgs, pkgInfo.Name) - case RepoTypeOBS: - obsPkgs = append(obsPkgs, pkgInfo) - case RepoTypeManual: - manualPkgs = append(manualPkgs, dep.Name) - } - } - - systemPkgs = o.appendMissingSystemPackages(systemPkgs, openSUSENiriRuntimePackages(wm, disabledFlags)) - - return systemPkgs, obsPkgs, manualPkgs, variantMap -} - -func openSUSENiriRuntimePackages(wm deps.WindowManager, disabledFlags map[string]bool) []string { - if wm != deps.WindowManagerNiri || disabledFlags["niri"] { - return nil - } - - return []string{openSUSENiriWaylandServerPackage} -} - -func (o *OpenSUSEDistribution) appendMissingSystemPackages(systemPkgs []string, extraPkgs []string) []string { - for _, pkg := range extraPkgs { - if slices.Contains(systemPkgs, pkg) || o.packageInstalled(pkg) { - continue - } - - o.log(fmt.Sprintf("Adding openSUSE runtime package: %s", pkg)) - systemPkgs = append(systemPkgs, pkg) - } - - return systemPkgs -} - -func (o *OpenSUSEDistribution) extractPackageNames(packages []PackageMapping) []string { - names := make([]string, len(packages)) - for i, pkg := range packages { - names[i] = pkg.Name - } - return names -} - -func (o *OpenSUSEDistribution) enableOBSRepos(ctx context.Context, obsPkgs []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - enabledRepos := make(map[string]bool) - - osInfo, err := GetOSInfo() - if err != nil { - return fmt.Errorf("failed to get OS info: %w", err) - } - - obsDistroVersion := "openSUSE_Tumbleweed" - switch osInfo.Distribution.ID { - case "opensuse-leap": - obsDistroVersion = fmt.Sprintf("openSUSE_Leap_%s", osInfo.VersionID) - case "opensuse-slowroll": - obsDistroVersion = "openSUSE_Slowroll" - } - - for _, pkg := range obsPkgs { - if pkg.RepoURL != "" && !enabledRepos[pkg.RepoURL] { - o.log(fmt.Sprintf("Enabling OBS repository: %s", pkg.RepoURL)) - - // RepoURL format: "home:AvengeMedia:danklinux" - repoPath := strings.ReplaceAll(pkg.RepoURL, ":", ":/") - repoName := strings.ReplaceAll(pkg.RepoURL, ":", "-") - repoURL := fmt.Sprintf("https://download.opensuse.org/repositories/%s/%s/%s.repo", - repoPath, obsDistroVersion, pkg.RepoURL) - - checkCmd := exec.CommandContext(ctx, "zypper", "repos", repoName) - if checkCmd.Run() == nil { - o.log(fmt.Sprintf("OBS repo %s already exists, skipping", pkg.RepoURL)) - enabledRepos[pkg.RepoURL] = true - continue - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.20, - Step: fmt.Sprintf("Enabling OBS repo %s...", pkg.RepoURL), - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo zypper addrepo %s", repoURL), - } - - cmd := ExecSudoCommand(ctx, sudoPassword, - fmt.Sprintf("zypper addrepo -f %s", repoURL)) - if err := o.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.20, 0.22); err != nil { - o.log(fmt.Sprintf("OBS repo %s add failed (may already exist): %v", pkg.RepoURL, err)) - } - - enabledRepos[pkg.RepoURL] = true - o.log(fmt.Sprintf("OBS repo %s enabled successfully", pkg.RepoURL)) - } - } - - // Refresh repositories with GPG auto-import - if len(enabledRepos) > 0 { - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.25, - Step: "Refreshing repositories...", - NeedsSudo: true, - CommandInfo: "sudo zypper --gpg-auto-import-keys refresh", - } - - refreshCmd := ExecSudoCommand(ctx, sudoPassword, "zypper --gpg-auto-import-keys refresh") - if err := o.runWithProgress(refreshCmd, progressChan, PhaseSystemPackages, 0.25, 0.27); err != nil { - return fmt.Errorf("failed to refresh repositories: %w", err) - } - } - - return nil -} - -func isOpenSUSEInstallMediaURI(uri string) bool { - normalizedURI := strings.ToLower(strings.TrimSpace(uri)) - - return strings.HasPrefix(normalizedURI, "cd:/") || - strings.HasPrefix(normalizedURI, "dvd:/") || - strings.HasPrefix(normalizedURI, "hd:/") || - strings.HasPrefix(normalizedURI, "iso:/") -} - -func parseZypperInstallMediaAliases(output string) []string { - var aliases []string - - for _, line := range strings.Split(output, "\n") { - line = strings.TrimSpace(line) - if line == "" || !strings.Contains(line, "|") { - continue - } - - parts := strings.Split(line, "|") - if len(parts) < 7 { - continue - } - - for i := range parts { - parts[i] = strings.TrimSpace(parts[i]) - } - - alias := parts[1] - enabled := strings.ToLower(parts[3]) - uri := parts[len(parts)-1] - - if alias == "" || strings.EqualFold(alias, "alias") { - continue - } - if enabled != "" && enabled != "yes" { - continue - } - if !isOpenSUSEInstallMediaURI(uri) { - continue - } - - aliases = append(aliases, alias) - } - - return aliases -} - -func (o *OpenSUSEDistribution) disableInstallMediaRepos(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - listCmd := exec.CommandContext(ctx, "zypper", "repos", "-u") - output, err := listCmd.CombinedOutput() - if err != nil { - o.log(fmt.Sprintf("Warning: failed to list zypper repositories: %s", strings.TrimSpace(string(output)))) - return fmt.Errorf("failed to list zypper repositories: %w", err) - } - - aliases := parseZypperInstallMediaAliases(string(output)) - if len(aliases) == 0 { - return nil - } - - o.log(fmt.Sprintf("Disabling install media repositories: %s", strings.Join(aliases, ", "))) - progressChan <- InstallProgressMsg{ - Phase: PhasePrerequisites, - Progress: 0.055, - Step: "Disabling install media repositories...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo zypper modifyrepo -d %s", strings.Join(aliases, " ")), - LogOutput: fmt.Sprintf("Disabling install media repositories: %s", strings.Join(aliases, ", ")), - } - - for _, alias := range aliases { - cmd := ExecSudoCommand(ctx, sudoPassword, fmt.Sprintf("zypper modifyrepo -d '%s'", escapeSingleQuotes(alias))) - repoOutput, err := cmd.CombinedOutput() - if err != nil { - o.log(fmt.Sprintf("Failed to disable install media repo %s: %s", alias, strings.TrimSpace(string(repoOutput)))) - return fmt.Errorf("failed to disable install media repo %s: %w", alias, err) - } - o.log(fmt.Sprintf("Disabled install media repo %s: %s", alias, strings.TrimSpace(string(repoOutput)))) - } - - return nil -} - -func (o *OpenSUSEDistribution) zypperInstallArgs(packages []string, minimal bool) []string { - args := []string{"zypper", "install", "-y"} - if minimal { - args = append(args, "--no-recommends") - } - return append(args, packages...) -} - -func (o *OpenSUSEDistribution) installZypperPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg, phase InstallPhase, step string, startProgress float64, endProgress float64) error { - if len(packages) == 0 { - return nil - } - - o.log(fmt.Sprintf("Installing zypper packages: %s", strings.Join(packages, ", "))) - - groups := orderedMinimalInstallGroups(packages) - totalGroups := len(groups) - - groupIndex := 0 - installGroup := func(groupPackages []string, minimal bool) error { - if len(groupPackages) == 0 { - return nil - } - - groupIndex++ - groupStart := startProgress - groupEnd := endProgress - if totalGroups > 1 { - midpoint := startProgress + ((endProgress - startProgress) / 2) - if groupIndex == 1 { - groupEnd = midpoint - } else { - groupStart = midpoint - } - } - - args := o.zypperInstallArgs(groupPackages, minimal) - progressChan <- InstallProgressMsg{ - Phase: phase, - Progress: groupStart, - Step: step, - IsComplete: false, - NeedsSudo: true, - CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")), - } - - cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " ")) - return o.runWithProgress(cmd, progressChan, phase, groupStart, groupEnd) - } - - for _, group := range groups { - if err := installGroup(group.packages, group.minimal); err != nil { - return err - } - } - return nil -} - -func (o *OpenSUSEDistribution) installQuickshell(ctx context.Context, variant deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - o.log("Installing quickshell from source (with openSUSE-specific build flags)...") - - homeDir := os.Getenv("HOME") - if homeDir == "" { - return fmt.Errorf("HOME environment variable not set") - } - - cacheDir := filepath.Join(homeDir, ".cache", "dankinstall") - if err := os.MkdirAll(cacheDir, 0o755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - tmpDir := filepath.Join(cacheDir, "quickshell-build") - if err := os.MkdirAll(tmpDir, 0o755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.1, - Step: "Cloning quickshell repository...", - IsComplete: false, - CommandInfo: "git clone https://github.com/quickshell-mirror/quickshell.git", - } - - var cloneCmd *exec.Cmd - if forceQuickshellGit || variant == deps.VariantGit { - cloneCmd = exec.CommandContext(ctx, "git", "clone", "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } else { - latestTag := o.getLatestQuickshellTag(ctx) - if latestTag != "" { - o.log(fmt.Sprintf("Using latest quickshell tag: %s", latestTag)) - cloneCmd = exec.CommandContext(ctx, "git", "clone", "--branch", latestTag, "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } else { - o.log("Warning: failed to fetch latest tag, using default branch") - cloneCmd = exec.CommandContext(ctx, "git", "clone", "https://github.com/quickshell-mirror/quickshell.git", tmpDir) - } - } - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone quickshell: %w", err) - } - - buildDir := tmpDir + "/build" - if err := os.MkdirAll(buildDir, 0o755); err != nil { - return fmt.Errorf("failed to create build directory: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.3, - Step: "Configuring quickshell build (with openSUSE flags)...", - IsComplete: false, - CommandInfo: "cmake -B build -S . -G Ninja", - } - - // Get optflags from rpm - optflagsCmd := exec.CommandContext(ctx, "rpm", "--eval", "%{optflags}") - optflagsOutput, err := optflagsCmd.Output() - optflags := strings.TrimSpace(string(optflagsOutput)) - if err != nil || optflags == "" { - o.log("Warning: Could not get optflags from rpm, using default -O2 -g") - optflags = "-O2 -g" - } - - // Set openSUSE-specific CFLAGS - customCFLAGS := fmt.Sprintf("%s -I/usr/include/wayland", optflags) - - configureCmd := exec.CommandContext(ctx, "cmake", "-GNinja", "-B", "build", - "-DCMAKE_BUILD_TYPE=RelWithDebInfo", - "-DCRASH_REPORTER=off", - "-DCMAKE_CXX_STANDARD=20") - configureCmd.Dir = tmpDir - configureCmd.Env = append(os.Environ(), - "TMPDIR="+cacheDir, - "CFLAGS="+customCFLAGS, - "CXXFLAGS="+customCFLAGS) - - o.log(fmt.Sprintf("Using CFLAGS: %s", customCFLAGS)) - - output, err := configureCmd.CombinedOutput() - if err != nil { - o.log(fmt.Sprintf("cmake configure failed. Output:\n%s", string(output))) - return fmt.Errorf("failed to configure quickshell: %w\nCMake output:\n%s", err, string(output)) - } - - o.log(fmt.Sprintf("cmake configure successful. Output:\n%s", string(output))) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.4, - Step: "Building quickshell (this may take a while)...", - IsComplete: false, - CommandInfo: "cmake --build build", - } - - buildCmd := exec.CommandContext(ctx, "cmake", "--build", "build") - buildCmd.Dir = tmpDir - buildCmd.Env = append(os.Environ(), - "TMPDIR="+cacheDir, - "CFLAGS="+customCFLAGS, - "CXXFLAGS="+customCFLAGS) - if err := o.runWithProgressStep(buildCmd, progressChan, PhaseSystemPackages, 0.4, 0.8, "Building quickshell..."); err != nil { - return fmt.Errorf("failed to build quickshell: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.8, - Step: "Installing quickshell...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo cmake --install build", - } - - installCmd := ExecSudoCommand(ctx, sudoPassword, "cmake --install build") - installCmd.Dir = tmpDir - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install quickshell: %w", err) - } - - o.log("quickshell installed successfully from source") - return nil -} - -func (o *OpenSUSEDistribution) installRust(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if o.commandExists("cargo") { - return nil - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.82, - Step: "Installing rustup...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo zypper install rustup", - } - - rustupInstallCmd := ExecSudoCommand(ctx, sudoPassword, "zypper install -y rustup") - if err := o.runWithProgress(rustupInstallCmd, progressChan, PhaseSystemPackages, 0.82, 0.83); err != nil { - return fmt.Errorf("failed to install rustup: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.83, - Step: "Installing stable Rust toolchain...", - IsComplete: false, - CommandInfo: "rustup install stable", - } - - rustInstallCmd := exec.CommandContext(ctx, "bash", "-c", "rustup install stable && rustup default stable") - if err := o.runWithProgress(rustInstallCmd, progressChan, PhaseSystemPackages, 0.83, 0.84); err != nil { - return fmt.Errorf("failed to install Rust toolchain: %w", err) - } - - if !o.commandExists("cargo") { - o.log("Warning: cargo not found in PATH after Rust installation, trying to source environment") - } - - return nil -} - -func (o *OpenSUSEDistribution) InstallManualPackages(ctx context.Context, packages []string, variantMap map[string]deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - if len(packages) == 0 { - return nil - } - - o.log(fmt.Sprintf("Installing manual packages: %s", strings.Join(packages, ", "))) - - for _, pkg := range packages { - if pkg == "matugen" { - if err := o.installRust(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install Rust: %w", err) - } - break - } - } - - for _, pkg := range packages { - variant := variantMap[pkg] - if pkg == "quickshell" { - if err := o.installQuickshell(ctx, variant, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install quickshell: %w", err) - } - } else { - if err := o.ManualPackageInstaller.InstallManualPackages(ctx, []string{pkg}, variantMap, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install %s: %w", pkg, err) - } - } - } - - return nil -} -- cgit v1.3