Files
miasma-installer/examples/danklinux/CONTRIBUTING_DISTRO.md
tumillanino 6fd2f42ec2
Some checks failed
Build / build (push) Failing after 4m53s
added required partitioning package installations
2025-11-12 16:01:17 +11:00

7.3 KiB

Adding New Linux Distributions

This guide explains how to add support for new Linux distributions to the dankdots installer using the new consolidated architecture.

Architecture Overview

The codebase uses a simple, consolidated approach where each distribution is completely self-contained:

  • All-in-One (internal/distros/{distro}.go) - Complete distribution implementation
  • Auto-Registration - Distributions register themselves via init() functions
  • Shared Base - Common functionality inherited from BaseDistribution

Adding Support

Method 1: Use Existing Implementation (Derivatives)

For distros that are derivatives (like CachyOS being Arch-based), you can register them to use an existing implementation.

Example: Adding CachyOS (Arch-based)

// internal/distros/arch.go - add to the init function
func init() {
    Register("arch", "#1793D1", func(config DistroConfig, logChan chan<- string) Distribution {
        return NewArchDistribution(config, logChan)
    })
    Register("cachyos", "#318CE7", func(config DistroConfig, logChan chan<- string) Distribution {
        return NewArchDistribution(config, logChan) // CachyOS uses Arch implementation but different color
    })
}

That's it! CachyOS now uses Arch's detection and installation logic.

Example: Adding Ubuntu derivatives

// internal/distros/ubuntu.go (after you create it)
func init() {
    Register("ubuntu", "#E95420", func(config DistroConfig, logChan chan<- string) Distribution {
        return NewUbuntuDistribution(config, logChan)
    })
    Register("kubuntu", "#0079C1", func(config DistroConfig, logChan chan<- string) Distribution {
        return NewUbuntuDistribution(config, logChan) // Kubuntu uses Ubuntu implementation but different color
    })
    Register("xubuntu", "#2F5BEA", func(config DistroConfig, logChan chan<- string) Distribution {
        return NewUbuntuDistribution(config, logChan) // Xubuntu uses Ubuntu implementation but different color
    })
    Register("pop", "#48B9C7", func(config DistroConfig, logChan chan<- string) Distribution {
        return NewUbuntuDistribution(config, logChan) // Pop!_OS uses Ubuntu implementation but different color
    })
}

Method 2: Create New Implementation

For entirely new distribution families, create a complete implementation:

Example: Adding openSUSE

Create internal/distros/opensuse.go:

package distros

import (
    "context"
    "os/exec"
    "strings"

    "github.com/AvengeMedia/danklinux/internal/deps"
    "github.com/AvengeMedia/danklinux/internal/installer"
)

func init() {
    Register("opensuse-leap", "#73BA25", func(config DistroConfig, logChan chan<- string) Distribution {
        return NewOpenSUSEDistribution(config, logChan)
    })
    Register("opensuse-tumbleweed", "#73BA25", func(config DistroConfig, logChan chan<- string) Distribution {
        return NewOpenSUSEDistribution(config, logChan)
    })
}

type OpenSUSEDistribution struct {
    *BaseDistribution
    *ManualPackageInstaller
    config DistroConfig
}

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) GetPackageManager() PackageManagerType {
    return PackageManagerZypper
}

func (o *OpenSUSEDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping {
    return map[string]PackageMapping{
        "git":      {Name: "git", Repository: RepoTypeSystem},
        "ghostty":  {Name: "ghostty", Repository: RepoTypeManual}, // Build from source
        "kitty":    {Name: "kitty", Repository: RepoTypeSystem},
        // ... map all required packages to openSUSE equivalents
    }
}

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
    
    // Use base methods for common functionality
    dependencies = append(dependencies, o.detectDMS())
    dependencies = append(dependencies, o.detectSpecificTerminal(terminal))
    dependencies = append(dependencies, o.detectGit())
    // ... add openSUSE-specific detection
    
    return dependencies, nil
}

func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, progressChan chan<- installer.InstallProgressMsg) error {
    // Implement installation logic using zypper
    // Use o.InstallManualPackages() for source builds
    return nil
}

func (o *OpenSUSEDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- installer.InstallProgressMsg) error {
    // Install build tools, enable repositories, etc.
    return nil
}

func (o *OpenSUSEDistribution) packageInstalled(pkg string) bool {
    cmd := exec.Command("rpm", "-q", pkg)
    err := cmd.Run()
    return err == nil
}

Repository Types

The system supports these repository types:

  • RepoTypeSystem - Main system repository (zypper, apt, dnf, pacman)
  • RepoTypeAUR - Arch User Repository
  • RepoTypeCOPR - Fedora COPR
  • RepoTypePPA - Ubuntu PPA
  • RepoTypeManual - Build from source

Package Manager Support

To add a new package manager, add it to internal/distros/interface.go:

const (
    PackageManagerPacman PackageManagerType = "pacman"
    PackageManagerDNF    PackageManagerType = "dnf"
    PackageManagerAPT    PackageManagerType = "apt"
    PackageManagerZypper PackageManagerType = "zypper"
    PackageManagerPortage PackageManagerType = "portage" // Add new ones here
)

Testing Your Implementation

  1. Build: go build -o dankdots ./cmd/main.go
  2. Test on target distribution
  3. Verify all packages detect and install correctly
  4. Test both window managers (Hyprland, Niri)
  5. Test both terminals (Ghostty, Kitty)

Detection Process

The system automatically detects supported distributions by:

  1. Reading /etc/os-release for the ID field
  2. Looking up the ID in the distribution registry
  3. Creating an instance using the registered constructor function

No hardcoded lists to maintain - everything is driven by the registry!

Benefits of New Architecture

  • Single file per distro - All logic in one place
  • Auto-registration - No factory methods to update
  • Shared functionality - Inherit common features
  • No duplication - Manual builds and fonts are shared
  • Easy derivatives - One line to support a new derivative

Contributing

  1. Fork the repository
  2. Create your distribution file in internal/distros/
  3. Test thoroughly on your target distribution
  4. Submit a pull request with example output

The maintainers will review and provide feedback. Thank you for expanding dankdots support!