1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-06 05:25:41 -05:00

Add Debian / Ubuntu / OpenSuse support to DankInstaller

This commit is contained in:
purian23
2025-12-03 23:41:17 -05:00
parent 5a53447272
commit 34c0bba130
4 changed files with 373 additions and 44 deletions

View File

@@ -70,7 +70,6 @@ func (d *DebianDistribution) DetectDependenciesWithTerminal(ctx context.Context,
dependencies = append(dependencies, d.detectMatugen()) dependencies = append(dependencies, d.detectMatugen())
dependencies = append(dependencies, d.detectDgop()) dependencies = append(dependencies, d.detectDgop())
dependencies = append(dependencies, d.detectHyprpicker())
dependencies = append(dependencies, d.detectClipboardTools()...) dependencies = append(dependencies, d.detectClipboardTools()...)
return dependencies, nil return dependencies, nil
@@ -139,7 +138,12 @@ func (d *DebianDistribution) packageInstalled(pkg string) bool {
} }
func (d *DebianDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { func (d *DebianDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping {
return d.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant))
}
func (d *DebianDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping {
packages := map[string]PackageMapping{ packages := map[string]PackageMapping{
// Standard APT packages
"git": {Name: "git", Repository: RepoTypeSystem}, "git": {Name: "git", Repository: RepoTypeSystem},
"kitty": {Name: "kitty", Repository: RepoTypeSystem}, "kitty": {Name: "kitty", Repository: RepoTypeSystem},
"alacritty": {Name: "alacritty", Repository: RepoTypeSystem}, "alacritty": {Name: "alacritty", Repository: RepoTypeSystem},
@@ -148,24 +152,54 @@ func (d *DebianDistribution) GetPackageMapping(wm deps.WindowManager) map[string
"mate-polkit": {Name: "mate-polkit", Repository: RepoTypeSystem}, "mate-polkit": {Name: "mate-polkit", Repository: RepoTypeSystem},
"accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem}, "accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem},
"dms (DankMaterialShell)": {Name: "dms", Repository: RepoTypeManual, BuildFunc: "installDankMaterialShell"}, // DMS packages from OBS with variant support
"niri": {Name: "niri", Repository: RepoTypeManual, BuildFunc: "installNiri"}, "dms (DankMaterialShell)": d.getDmsMapping(variants["dms (DankMaterialShell)"]),
"quickshell": {Name: "quickshell", Repository: RepoTypeManual, BuildFunc: "installQuickshell"}, "quickshell": d.getQuickshellMapping(variants["quickshell"]),
"matugen": {Name: "matugen", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"},
"dgop": {Name: "dgop", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"},
"cliphist": {Name: "cliphist", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"},
// Keep ghostty as manual (no OBS package yet)
"ghostty": {Name: "ghostty", Repository: RepoTypeManual, BuildFunc: "installGhostty"}, "ghostty": {Name: "ghostty", Repository: RepoTypeManual, BuildFunc: "installGhostty"},
"matugen": {Name: "matugen", Repository: RepoTypeManual, BuildFunc: "installMatugen"},
"dgop": {Name: "dgop", Repository: RepoTypeManual, BuildFunc: "installDgop"},
"cliphist": {Name: "cliphist", Repository: RepoTypeManual, BuildFunc: "installCliphist"},
"hyprpicker": {Name: "hyprpicker", Repository: RepoTypeManual, BuildFunc: "installHyprpicker"},
} }
if wm == deps.WindowManagerNiri { if wm == deps.WindowManagerNiri {
packages["niri"] = PackageMapping{Name: "niri", Repository: RepoTypeManual, BuildFunc: "installNiri"} niriVariant := variants["niri"]
packages["xwayland-satellite"] = PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeManual, BuildFunc: "installXwaylandSatellite"} packages["niri"] = d.getNiriMapping(niriVariant)
packages["xwayland-satellite"] = d.getXwaylandSatelliteMapping(niriVariant)
} }
return packages return packages
} }
func (d *DebianDistribution) 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 (d *DebianDistribution) 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 (d *DebianDistribution) 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: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}
}
func (d *DebianDistribution) 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: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}
}
func (d *DebianDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { func (d *DebianDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhasePrerequisites, Phase: PhasePrerequisites,
@@ -238,8 +272,23 @@ func (d *DebianDistribution) InstallPackages(ctx context.Context, dependencies [
return fmt.Errorf("failed to install prerequisites: %w", err) return fmt.Errorf("failed to install prerequisites: %w", err)
} }
systemPkgs, manualPkgs, variantMap := d.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags) systemPkgs, obsPkgs, manualPkgs, variantMap := d.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 := d.enableOBSRepos(ctx, obsPkgs, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to enable OBS repositories: %w", err)
}
}
// System Packages
if len(systemPkgs) > 0 { if len(systemPkgs) > 0 {
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages, Phase: PhaseSystemPackages,
@@ -254,6 +303,22 @@ func (d *DebianDistribution) InstallPackages(ctx context.Context, dependencies [
} }
} }
// OBS Packages
obsPkgNames := d.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 := d.installAPTPackages(ctx, obsPkgNames, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install OBS packages: %w", err)
}
}
// Manual Builds
if len(manualPkgs) > 0 { if len(manualPkgs) > 0 {
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages, Phase: PhaseSystemPackages,
@@ -297,8 +362,9 @@ func (d *DebianDistribution) InstallPackages(ctx context.Context, dependencies [
return nil return nil
} }
func (d *DebianDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []string, map[string]deps.PackageVariant) { func (d *DebianDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []PackageMapping, []string, map[string]deps.PackageVariant) {
systemPkgs := []string{} systemPkgs := []string{}
obsPkgs := []PackageMapping{}
manualPkgs := []string{} manualPkgs := []string{}
variantMap := make(map[string]deps.PackageVariant) variantMap := make(map[string]deps.PackageVariant)
@@ -306,7 +372,7 @@ func (d *DebianDistribution) categorizePackages(dependencies []deps.Dependency,
variantMap[dep.Name] = dep.Variant variantMap[dep.Name] = dep.Variant
} }
packageMap := d.GetPackageMapping(wm) packageMap := d.GetPackageMappingWithVariants(wm, variantMap)
for _, dep := range dependencies { for _, dep := range dependencies {
if disabledFlags[dep.Name] { if disabledFlags[dep.Name] {
@@ -326,12 +392,116 @@ func (d *DebianDistribution) categorizePackages(dependencies []deps.Dependency,
switch pkgInfo.Repository { switch pkgInfo.Repository {
case RepoTypeSystem: case RepoTypeSystem:
systemPkgs = append(systemPkgs, pkgInfo.Name) systemPkgs = append(systemPkgs, pkgInfo.Name)
case RepoTypeOBS:
obsPkgs = append(obsPkgs, pkgInfo)
case RepoTypeManual: case RepoTypeManual:
manualPkgs = append(manualPkgs, dep.Name) manualPkgs = append(manualPkgs, dep.Name)
} }
} }
return systemPkgs, manualPkgs, variantMap return systemPkgs, obsPkgs, manualPkgs, variantMap
}
func (d *DebianDistribution) extractPackageNames(packages []PackageMapping) []string {
names := make([]string, len(packages))
for i, pkg := range packages {
names[i] = pkg.Name
}
return names
}
func (d *DebianDistribution) 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)
}
// Determine Debian version for OBS repository URL
debianVersion := "Debian_13"
if osInfo.VersionID == "testing" {
debianVersion = "Debian_Testing"
}
for _, pkg := range obsPkgs {
if pkg.RepoURL != "" && !enabledRepos[pkg.RepoURL] {
d.log(fmt.Sprintf("Enabling OBS repository: %s", pkg.RepoURL))
// RepoURL format: "home:AvengeMedia:danklinux"
repoPath := strings.ReplaceAll(pkg.RepoURL, ":", ":/")
repoName := strings.ReplaceAll(pkg.RepoURL, ":", "-")
baseURL := fmt.Sprintf("https://download.opensuse.org/repositories/%s/%s", repoPath, debianVersion)
// Check if repository already exists
listFile := fmt.Sprintf("/etc/apt/sources.list.d/%s.list", repoName)
checkCmd := exec.CommandContext(ctx, "test", "-f", listFile)
if checkCmd.Run() == nil {
d.log(fmt.Sprintf("OBS repo %s already exists, skipping", pkg.RepoURL))
enabledRepos[pkg.RepoURL] = true
continue
}
keyringPath := fmt.Sprintf("/etc/apt/keyrings/%s.gpg", repoName)
// Create keyrings directory if it doesn't exist
mkdirCmd := ExecSudoCommand(ctx, sudoPassword, "mkdir -p /etc/apt/keyrings")
if err := mkdirCmd.Run(); err != nil {
d.log(fmt.Sprintf("Warning: failed to create keyrings directory: %v", err))
}
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.18,
Step: fmt.Sprintf("Adding OBS GPG key for %s...", pkg.RepoURL),
NeedsSudo: true,
CommandInfo: fmt.Sprintf("curl & gpg to add key for %s", pkg.RepoURL),
}
keyCmd := fmt.Sprintf("curl -fsSL %s/Release.key | gpg --dearmor -o %s", baseURL, keyringPath)
cmd := ExecSudoCommand(ctx, sudoPassword, keyCmd)
if err := d.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.18, 0.20); err != nil {
return fmt.Errorf("failed to add OBS GPG key for %s: %w", pkg.RepoURL, err)
}
// Add repository
repoLine := fmt.Sprintf("deb [signed-by=%s] %s/ /", keyringPath, baseURL)
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.20,
Step: fmt.Sprintf("Adding OBS repository %s...", pkg.RepoURL),
NeedsSudo: true,
CommandInfo: fmt.Sprintf("echo '%s' | sudo tee %s", repoLine, listFile),
}
addRepoCmd := ExecSudoCommand(ctx, sudoPassword,
fmt.Sprintf("echo '%s' | tee %s", repoLine, listFile))
if err := d.runWithProgress(addRepoCmd, progressChan, PhaseSystemPackages, 0.20, 0.22); err != nil {
return fmt.Errorf("failed to add OBS repo %s: %w", pkg.RepoURL, err)
}
enabledRepos[pkg.RepoURL] = true
d.log(fmt.Sprintf("OBS repo %s enabled successfully", pkg.RepoURL))
}
}
if len(enabledRepos) > 0 {
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.25,
Step: "Updating package lists...",
NeedsSudo: true,
CommandInfo: "sudo apt-get update",
}
updateCmd := ExecSudoCommand(ctx, sudoPassword, "apt-get update")
if err := d.runWithProgress(updateCmd, progressChan, PhaseSystemPackages, 0.25, 0.27); err != nil {
return fmt.Errorf("failed to update package lists after adding OBS repos: %w", err)
}
}
return nil
} }
func (d *DebianDistribution) installAPTPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { func (d *DebianDistribution) installAPTPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error {

View File

@@ -39,6 +39,7 @@ const (
RepoTypeAUR RepositoryType = "aur" // Arch User Repository RepoTypeAUR RepositoryType = "aur" // Arch User Repository
RepoTypeCOPR RepositoryType = "copr" // Fedora COPR RepoTypeCOPR RepositoryType = "copr" // Fedora COPR
RepoTypePPA RepositoryType = "ppa" // Ubuntu PPA RepoTypePPA RepositoryType = "ppa" // Ubuntu PPA
RepoTypeOBS RepositoryType = "obs" // OpenBuild Service (Debian/OpenSUSE)
RepoTypeFlake RepositoryType = "flake" // Nix flake RepoTypeFlake RepositoryType = "flake" // Nix flake
RepoTypeGURU RepositoryType = "guru" // Gentoo GURU RepoTypeGURU RepositoryType = "guru" // Gentoo GURU
RepoTypeManual RepositoryType = "manual" // Manual build from source RepoTypeManual RepositoryType = "manual" // Manual build from source

View File

@@ -82,7 +82,6 @@ func (o *OpenSUSEDistribution) DetectDependenciesWithTerminal(ctx context.Contex
// Base detections (common across distros) // Base detections (common across distros)
dependencies = append(dependencies, o.detectMatugen()) dependencies = append(dependencies, o.detectMatugen())
dependencies = append(dependencies, o.detectDgop()) dependencies = append(dependencies, o.detectDgop())
dependencies = append(dependencies, o.detectHyprpicker())
dependencies = append(dependencies, o.detectClipboardTools()...) dependencies = append(dependencies, o.detectClipboardTools()...)
return dependencies, nil return dependencies, nil
@@ -138,13 +137,12 @@ func (o *OpenSUSEDistribution) GetPackageMappingWithVariants(wm deps.WindowManag
"mate-polkit": {Name: "mate-polkit", Repository: RepoTypeSystem}, "mate-polkit": {Name: "mate-polkit", Repository: RepoTypeSystem},
"accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem}, "accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem},
"cliphist": {Name: "cliphist", Repository: RepoTypeSystem}, "cliphist": {Name: "cliphist", Repository: RepoTypeSystem},
"hyprpicker": {Name: "hyprpicker", Repository: RepoTypeSystem},
// Manual builds // DMS packages from OBS
"dms (DankMaterialShell)": {Name: "dms", Repository: RepoTypeManual, BuildFunc: "installDankMaterialShell"}, "dms (DankMaterialShell)": o.getDmsMapping(variants["dms (DankMaterialShell)"]),
"dgop": {Name: "dgop", Repository: RepoTypeManual, BuildFunc: "installDgop"}, "quickshell": o.getQuickshellMapping(variants["quickshell"]),
"quickshell": {Name: "quickshell", Repository: RepoTypeManual, BuildFunc: "installQuickshell"}, "matugen": {Name: "matugen", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"},
"matugen": {Name: "matugen", Repository: RepoTypeManual, BuildFunc: "installMatugen"}, "dgop": {Name: "dgop", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"},
} }
switch wm { switch wm {
@@ -156,13 +154,43 @@ func (o *OpenSUSEDistribution) GetPackageMappingWithVariants(wm deps.WindowManag
packages["grimblast"] = PackageMapping{Name: "grimblast", Repository: RepoTypeManual, BuildFunc: "installGrimblast"} packages["grimblast"] = PackageMapping{Name: "grimblast", Repository: RepoTypeManual, BuildFunc: "installGrimblast"}
packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem} packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem}
case deps.WindowManagerNiri: case deps.WindowManagerNiri:
packages["niri"] = PackageMapping{Name: "niri", Repository: RepoTypeSystem} // Niri stable has native package support on openSUSE
packages["xwayland-satellite"] = PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeSystem} niriVariant := variants["niri"]
packages["niri"] = o.getNiriMapping(niriVariant)
packages["xwayland-satellite"] = o.getXwaylandSatelliteMapping(niriVariant)
} }
return packages 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 { func (o *OpenSUSEDistribution) detectXwaylandSatellite() deps.Dependency {
status := deps.StatusMissing status := deps.StatusMissing
if o.commandExists("xwayland-satellite") { if o.commandExists("xwayland-satellite") {
@@ -294,9 +322,23 @@ func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies
return fmt.Errorf("failed to install prerequisites: %w", err) return fmt.Errorf("failed to install prerequisites: %w", err)
} }
systemPkgs, manualPkgs, variantMap := o.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags) systemPkgs, obsPkgs, manualPkgs, variantMap := o.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
// Phase 2: System Packages (Zypper) // 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 { if len(systemPkgs) > 0 {
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages, Phase: PhaseSystemPackages,
@@ -311,7 +353,22 @@ func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies
} }
} }
// Phase 3: Manual Builds // 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); err != nil {
return fmt.Errorf("failed to install OBS packages: %w", err)
}
}
// Manual Builds
if len(manualPkgs) > 0 { if len(manualPkgs) > 0 {
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages, Phase: PhaseSystemPackages,
@@ -325,7 +382,7 @@ func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies
} }
} }
// Phase 4: Configuration // Configuration
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhaseConfiguration, Phase: PhaseConfiguration,
Progress: 0.90, Progress: 0.90,
@@ -334,7 +391,7 @@ func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies
LogOutput: "Starting post-installation configuration...", LogOutput: "Starting post-installation configuration...",
} }
// Phase 5: Complete // Complete
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhaseComplete, Phase: PhaseComplete,
Progress: 1.0, Progress: 1.0,
@@ -346,8 +403,9 @@ func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies
return nil return nil
} }
func (o *OpenSUSEDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []string, map[string]deps.PackageVariant) { 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{} systemPkgs := []string{}
obsPkgs := []PackageMapping{}
manualPkgs := []string{} manualPkgs := []string{}
variantMap := make(map[string]deps.PackageVariant) variantMap := make(map[string]deps.PackageVariant)
@@ -375,12 +433,80 @@ func (o *OpenSUSEDistribution) categorizePackages(dependencies []deps.Dependency
switch pkgInfo.Repository { switch pkgInfo.Repository {
case RepoTypeSystem: case RepoTypeSystem:
systemPkgs = append(systemPkgs, pkgInfo.Name) systemPkgs = append(systemPkgs, pkgInfo.Name)
case RepoTypeOBS:
obsPkgs = append(obsPkgs, pkgInfo)
case RepoTypeManual: case RepoTypeManual:
manualPkgs = append(manualPkgs, dep.Name) manualPkgs = append(manualPkgs, dep.Name)
} }
} }
return systemPkgs, manualPkgs, variantMap return systemPkgs, obsPkgs, manualPkgs, variantMap
}
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)
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/openSUSE_Tumbleweed/%s.repo",
repoPath, 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 {
return fmt.Errorf("failed to enable OBS repo %s: %w", 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 (o *OpenSUSEDistribution) installZypperPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error { func (o *OpenSUSEDistribution) installZypperPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error {

View File

@@ -82,7 +82,6 @@ func (u *UbuntuDistribution) DetectDependenciesWithTerminal(ctx context.Context,
// Base detections (common across distros) // Base detections (common across distros)
dependencies = append(dependencies, u.detectMatugen()) dependencies = append(dependencies, u.detectMatugen())
dependencies = append(dependencies, u.detectDgop()) dependencies = append(dependencies, u.detectDgop())
dependencies = append(dependencies, u.detectHyprpicker())
dependencies = append(dependencies, u.detectClipboardTools()...) dependencies = append(dependencies, u.detectClipboardTools()...)
return dependencies, nil return dependencies, nil
@@ -151,6 +150,10 @@ func (u *UbuntuDistribution) packageInstalled(pkg string) bool {
} }
func (u *UbuntuDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { func (u *UbuntuDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping {
return u.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant))
}
func (u *UbuntuDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping {
packages := map[string]PackageMapping{ packages := map[string]PackageMapping{
// Standard APT packages // Standard APT packages
"git": {Name: "git", Repository: RepoTypeSystem}, "git": {Name: "git", Repository: RepoTypeSystem},
@@ -160,16 +163,16 @@ func (u *UbuntuDistribution) GetPackageMapping(wm deps.WindowManager) map[string
"xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem}, "xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem},
"mate-polkit": {Name: "mate-polkit", Repository: RepoTypeSystem}, "mate-polkit": {Name: "mate-polkit", Repository: RepoTypeSystem},
"accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem}, "accountsservice": {Name: "accountsservice", Repository: RepoTypeSystem},
"hyprpicker": {Name: "hyprpicker", Repository: RepoTypePPA, RepoURL: "ppa:cppiber/hyprland"},
// Manual builds (niri and quickshell likely not available in Ubuntu repos or PPAs) // DMS packages from PPAs
"dms (DankMaterialShell)": {Name: "dms", Repository: RepoTypeManual, BuildFunc: "installDankMaterialShell"}, "dms (DankMaterialShell)": u.getDmsMapping(variants["dms (DankMaterialShell)"]),
"niri": {Name: "niri", Repository: RepoTypeManual, BuildFunc: "installNiri"}, "quickshell": u.getQuickshellMapping(variants["quickshell"]),
"quickshell": {Name: "quickshell", Repository: RepoTypeManual, BuildFunc: "installQuickshell"}, "matugen": {Name: "matugen", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"},
"dgop": {Name: "dgop", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"},
"cliphist": {Name: "cliphist", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"},
// Keep ghostty as manual (no PPA available)
"ghostty": {Name: "ghostty", Repository: RepoTypeManual, BuildFunc: "installGhostty"}, "ghostty": {Name: "ghostty", Repository: RepoTypeManual, BuildFunc: "installGhostty"},
"matugen": {Name: "matugen", Repository: RepoTypeManual, BuildFunc: "installMatugen"},
"dgop": {Name: "dgop", Repository: RepoTypeManual, BuildFunc: "installDgop"},
"cliphist": {Name: "cliphist", Repository: RepoTypeManual, BuildFunc: "installCliphist"},
} }
switch wm { switch wm {
@@ -182,13 +185,42 @@ func (u *UbuntuDistribution) GetPackageMapping(wm deps.WindowManager) map[string
packages["grimblast"] = PackageMapping{Name: "grimblast", Repository: RepoTypeManual, BuildFunc: "installGrimblast"} packages["grimblast"] = PackageMapping{Name: "grimblast", Repository: RepoTypeManual, BuildFunc: "installGrimblast"}
packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem} packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem}
case deps.WindowManagerNiri: case deps.WindowManagerNiri:
packages["niri"] = PackageMapping{Name: "niri", Repository: RepoTypeManual, BuildFunc: "installNiri"} niriVariant := variants["niri"]
packages["xwayland-satellite"] = PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeManual, BuildFunc: "installXwaylandSatellite"} packages["niri"] = u.getNiriMapping(niriVariant)
packages["xwayland-satellite"] = u.getXwaylandSatelliteMapping(niriVariant)
} }
return packages return packages
} }
func (u *UbuntuDistribution) getDmsMapping(variant deps.PackageVariant) PackageMapping {
if variant == deps.VariantGit {
return PackageMapping{Name: "dms-git", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/dms-git"}
}
return PackageMapping{Name: "dms", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/dms"}
}
func (u *UbuntuDistribution) getQuickshellMapping(variant deps.PackageVariant) PackageMapping {
if forceQuickshellGit || variant == deps.VariantGit {
return PackageMapping{Name: "quickshell-git", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"}
}
return PackageMapping{Name: "quickshell", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"}
}
func (u *UbuntuDistribution) getNiriMapping(variant deps.PackageVariant) PackageMapping {
if variant == deps.VariantGit {
return PackageMapping{Name: "niri-git", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"}
}
return PackageMapping{Name: "niri", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"}
}
func (u *UbuntuDistribution) getXwaylandSatelliteMapping(variant deps.PackageVariant) PackageMapping {
if variant == deps.VariantGit {
return PackageMapping{Name: "xwayland-satellite-git", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"}
}
return PackageMapping{Name: "xwayland-satellite", Repository: RepoTypePPA, RepoURL: "ppa:avengemedia/danklinux"}
}
func (u *UbuntuDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { func (u *UbuntuDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhasePrerequisites, Phase: PhasePrerequisites,
@@ -365,7 +397,7 @@ func (u *UbuntuDistribution) categorizePackages(dependencies []deps.Dependency,
variantMap[dep.Name] = dep.Variant variantMap[dep.Name] = dep.Variant
} }
packageMap := u.GetPackageMapping(wm) packageMap := u.GetPackageMappingWithVariants(wm, variantMap)
for _, dep := range dependencies { for _, dep := range dependencies {
if disabledFlags[dep.Name] { if disabledFlags[dep.Name] {