ubuntu: fall back to archive Hyprland when the PPA lacks the series

Fixes #3899
Port 2.0

(cherry picked from commit 27a55349bc)
This commit is contained in:
purian23 authored and dux-ci[bot] committed 2026-10-28 04:12:39 +00:00
1 parent 1efbf723f8
commit 43cb8c70b7
1 file changed
+49 -3
+49 -3
View File
@@ -2,14 +2,20 @@ package distros
import (
"context"
"errors"
"fmt"
"net/http"
"os/exec"
"strings"
"time"
"github.com/duxlabs-dev/dux-shell/core/internal/deps"
"github.com/duxlabs-dev/dux-shell/core/internal/netfetch"
"github.com/duxlabs-dev/dux-shell/core/internal/privesc"
)
const hyprlandPPA = "ppa:cppiber/hyprland"
func init() {
Register("ubuntu", "#E95420", FamilyUbuntu, func(config DistroConfig, logChan chan<- string) Distribution {
return NewUbuntuDistribution(config, logChan)
@@ -149,10 +155,10 @@ func (u *UbuntuDistribution) GetPackageMappingWithVariants(wm deps.WindowManager
switch wm {
case deps.WindowManagerHyprland:
// Use the cppiber PPA for Hyprland
packages["hyprland"] = PackageMapping{Name: "hyprland", Repository: RepoTypePPA, RepoURL: "ppa:cppiber/hyprland"}
packages["hyprctl"] = PackageMapping{Name: "hyprland", Repository: RepoTypePPA, RepoURL: "ppa:cppiber/hyprland"}
packages["hyprland"] = PackageMapping{Name: "hyprland", Repository: RepoTypePPA, RepoURL: hyprlandPPA}
packages["hyprctl"] = PackageMapping{Name: "hyprland", Repository: RepoTypePPA, RepoURL: hyprlandPPA}
packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem}
packages["hyprland-guiutils"] = PackageMapping{Name: "hyprland-guiutils", Repository: RepoTypePPA, RepoURL: "ppa:cppiber/hyprland"}
packages["hyprland-guiutils"] = PackageMapping{Name: "hyprland-guiutils", Repository: RepoTypePPA, RepoURL: hyprlandPPA}
case deps.WindowManagerNiri:
niriVariant := variants["niri"]
packages["niri"] = u.getNiriMapping(niriVariant)
@@ -265,6 +271,7 @@ func (u *UbuntuDistribution) InstallPackages(ctx context.Context, dependencies [
}
systemPkgs, ppaPkgs, manualPkgs, variantMap := u.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
systemPkgs, ppaPkgs = u.fallbackUnpublishedPPAs(ctx, systemPkgs, ppaPkgs)
// Phase 2: Enable PPA repositories
if len(ppaPkgs) > 0 {
@@ -417,6 +424,45 @@ func (u *UbuntuDistribution) extractPackageNames(packages []PackageMapping) []st
return names
}
// Use the Ubuntu archive when the Hyprland PPA has no build for this series yet.
func (u *UbuntuDistribution) fallbackUnpublishedPPAs(ctx context.Context, systemPkgs []string, ppaPkgs []PackageMapping) ([]string, []PackageMapping) {
usesHyprlandPPA := false
for _, pkg := range ppaPkgs {
usesHyprlandPPA = usesHyprlandPPA || pkg.RepoURL == hyprlandPPA
}
if !usesHyprlandPPA {
return systemPkgs, ppaPkgs
}
osInfo, err := GetOSInfo()
if err != nil || osInfo.VersionCodename == "" || ppaPublishesSeries(ctx, hyprlandPPA, osInfo.VersionCodename) {
return systemPkgs, ppaPkgs
}
u.log(fmt.Sprintf("%s has no %s build, installing its packages from the Ubuntu archive", hyprlandPPA, osInfo.VersionCodename))
kept := ppaPkgs[:0]
for _, pkg := range ppaPkgs {
if pkg.RepoURL == hyprlandPPA {
systemPkgs = append(systemPkgs, pkg.Name)
continue
}
kept = append(kept, pkg)
}
return systemPkgs, kept
}
// Only a 404 counts as unpublished; network errors keep the PPA.
func ppaPublishesSeries(ctx context.Context, ppa, series string) bool {
owner, name, ok := strings.Cut(strings.TrimPrefix(ppa, "ppa:"), "/")
if !ok {
return true
}
url := fmt.Sprintf("https://ppa.launchpadcontent.net/%s/%s/ubuntu/dists/%s/Release", owner, name, series)
_, err := netfetch.Bytes(ctx, url, netfetch.Options{Timeout: 15 * time.Second})
var statusErr *netfetch.StatusError
return !errors.As(err, &statusErr) || statusErr.Code != http.StatusNotFound
}
func (u *UbuntuDistribution) enablePPARepos(ctx context.Context, ppaPkgs []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
enabledRepos := make(map[string]bool)