summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/opensuse.go
diff options
context:
space:
mode:
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/opensuse.go')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/distros/opensuse.go856
1 files changed, 0 insertions, 856 deletions
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
-}