From 8caccd09fd92fb18042b3356533203e902a8335c Mon Sep 17 00:00:00 2001 From: purian23 Date: Fri, 30 Oct 2026 07:32:31 -0400 Subject: [PATCH] distros: dux packaging rebrand sweep Port 2.0 (cherry picked from commit 40a129c9d6f75566073d1a5eb1d4fb3ca48a79e4) --- core/cmd/dms/commands_greeter.go | 22 +++--- core/internal/distros/base.go | 33 +++++++++ core/internal/distros/base_test.go | 54 ++++++++++++++ core/internal/distros/debian.go | 16 ++-- core/internal/distros/fedora.go | 74 +++++++++++++++---- core/internal/distros/immutable_test.go | 29 +++++++- core/internal/distros/opensuse.go | 16 ++-- core/internal/distros/ubuntu.go | 16 ++-- core/internal/distros/variant_swap.go | 19 +++-- core/internal/distros/variant_swap_test.go | 21 ++++-- core/internal/distros/void.go | 14 +--- .../server/sysupdate/backend_dnf_test.go | 8 +- distro/scripts/ppa-build.sh | 10 +-- distro/scripts/ppa-upload.sh | 2 +- distro/void/README.md | 18 ++--- quickshell/Modules/Settings/GreeterTab.qml | 2 +- 16 files changed, 253 insertions(+), 101 deletions(-) diff --git a/core/cmd/dms/commands_greeter.go b/core/cmd/dms/commands_greeter.go index 04b2f0b1b..c052b4c83 100644 --- a/core/cmd/dms/commands_greeter.go +++ b/core/cmd/dms/commands_greeter.go @@ -14,16 +14,16 @@ import ( var greeterCmd = &cobra.Command{ Use: "greeter", Short: "Deprecated: moved to the standalone dms-greeter binary", - Long: "The greeter has moved to the standalone dms-greeter package.\nThis command forwards to 'dms-greeter' when it is installed.", + Long: "The greeter has moved to the standalone dux-greeter package.\nThis command forwards to 'dms-greeter' when it is installed.", DisableFlagParsing: true, SilenceUsage: true, RunE: func(_ *cobra.Command, args []string) error { binary, err := exec.LookPath("dms-greeter") if err != nil { - return fmt.Errorf("'dms greeter' has moved to the standalone dms-greeter package.\n %s", greeterPackageInstallHint()) + return fmt.Errorf("'dms greeter' has moved to the standalone dux-greeter package.\n %s", greeterPackageInstallHint()) } if isLegacyWrapperScript(binary) { - return fmt.Errorf("'dms greeter' has moved to the standalone dms-greeter package; %s is the old wrapper script.\n %s", binary, greeterPackageInstallHint()) + return fmt.Errorf("'dms greeter' has moved to the standalone dux-greeter package; %s is the old wrapper script.\n %s", binary, greeterPackageInstallHint()) } fmt.Fprintln(os.Stderr, "warning: 'dms greeter' is deprecated; use 'dms-greeter' directly") return syscall.Exec(binary, append([]string{"dms-greeter"}, args...), os.Environ()) @@ -50,11 +50,11 @@ func isLegacyWrapperScript(path string) bool { func greeterPackageInstallHint() string { osInfo, err := distros.GetOSInfo() if err != nil { - return "Install package: dms-greeter" + return "Install package: dux-greeter" } config, exists := distros.Registry[osInfo.Distribution.ID] if !exists { - return "Install package: dms-greeter" + return "Install package: dux-greeter" } if config.ID == "pika" { @@ -63,19 +63,19 @@ func greeterPackageInstallHint() string { switch config.Family { case distros.FamilyDebian: - return "Install with 'sudo apt install dms-greeter' (requires Dux Labs OBS repo — see " + site.Docs + "/dms-greeter/installation#debian)" + return "Install with 'sudo apt install dux-greeter' (requires Dux Labs OBS repo — see " + site.Docs + "/dms-greeter/installation#debian)" case distros.FamilySUSE: - return "Install with 'sudo zypper install dms-greeter' (requires Dux Labs OBS repo — see " + site.Docs + "/dms-greeter/installation#opensuse--derivatives)" + return "Install with 'sudo zypper install dux-greeter' (requires Dux Labs OBS repo — see " + site.Docs + "/dms-greeter/installation#opensuse--derivatives)" case distros.FamilyUbuntu: repo := greeterRepoURL(config) - return fmt.Sprintf("Install with 'sudo apt install dms-greeter' (requires %s: sudo add-apt-repository %s)", repo, repo) + return fmt.Sprintf("Install with 'sudo apt install dux-greeter' (requires %s: sudo add-apt-repository %s)", repo, repo) case distros.FamilyFedora: - return fmt.Sprintf("Install with 'sudo dnf install dms-greeter' (requires COPR: sudo dnf copr enable %s)", greeterRepoURL(config)) + return fmt.Sprintf("Install with 'sudo dnf install dux-greeter' (requires COPR: sudo dnf copr enable %s)", greeterRepoURL(config)) case distros.FamilyArch: return "Install from AUR with 'paru -S dux-greeter-bin' or 'yay -S dux-greeter-bin'" case distros.FamilyVoid: - return "Install with 'sudo xbps-install -S dms-greeter' (requires DMS XBPS repo: echo 'repository=" + distros.VoidDepsRepo + "' | sudo tee /etc/xbps.d/dux.conf)" + return "Install with 'sudo xbps-install -S dux-greeter' (requires DMS XBPS repo: echo 'repository=" + distros.VoidDepsRepo + "' | sudo tee /etc/xbps.d/dux.conf)" default: - return "Install the dms-greeter package for your distribution" + return "Install the dux-greeter package for your distribution" } } diff --git a/core/internal/distros/base.go b/core/internal/distros/base.go index 6a17a7a40..bc654c6e3 100644 --- a/core/internal/distros/base.go +++ b/core/internal/distros/base.go @@ -113,6 +113,39 @@ func (b *BaseDistribution) detectDankCalendar() deps.Dependency { return b.detectOptionalPackage("dankcalendar", "Calendar application", b.commandExists("dcal") || b.commandExists("dankcalendar")) } +// renamedPackages maps distro packages to their pre-rename names. rpm -q, dpkg-query and xbps-query +// ignore Provides, so hosts that have not upgraded since the rename only match the old name. +var renamedPackages = map[string]string{ + "dux-greeter": "dms-greeter", + "dux-greeter-git": "dms-greeter-git", + "dux-search": "danksearch", + "dux-calendar": "dankcalendar", + "dux-calendar-git": "dankcalendar-git", + "dux-gop": "dgop", +} + +func installedName(installed func(string) bool, pkg string) (string, bool) { + if installed(pkg) { + return pkg, true + } + if old, ok := renamedPackages[pkg]; ok && installed(old) { + return old, true + } + return "", false +} + +func (b *BaseDistribution) detectGreeterPackage(installed func(string) bool) deps.Dependency { + _, gitInstalled := installedName(installed, "dux-greeter-git") + _, stableInstalled := installedName(installed, "dux-greeter") + dep := b.detectOptionalPackage("dms-greeter", "Dux Shell greetd greeter", gitInstalled || stableInstalled) + return withGitVariant(dep, gitInstalled) +} + +func (b *BaseDistribution) detectCalendarPackage(installed func(string) bool) deps.Dependency { + _, gitInstalled := installedName(installed, "dux-calendar-git") + return withGitVariant(b.detectDankCalendar(), gitInstalled) +} + func withGitVariant(dep deps.Dependency, gitInstalled bool) deps.Dependency { dep.CanToggle = true if gitInstalled { diff --git a/core/internal/distros/base_test.go b/core/internal/distros/base_test.go index 60c7d1426..d8a5bedc8 100644 --- a/core/internal/distros/base_test.go +++ b/core/internal/distros/base_test.go @@ -3,6 +3,7 @@ package distros import ( "os" "path/filepath" + "strings" "testing" "github.com/duxlabs-dev/dux-shell/core/internal/deps" @@ -73,3 +74,56 @@ func TestParseHyprlandVersion(t *testing.T) { } } } + +func TestDetectGreeterPackageAcceptsOldNames(t *testing.T) { + cases := []struct { + installed string + wantInstalled bool + wantVariant deps.PackageVariant + }{ + {"", false, deps.VariantStable}, + {"dms-greeter", true, deps.VariantStable}, + {"dux-greeter", true, deps.VariantStable}, + {"dms-greeter-git", true, deps.VariantGit}, + {"dux-greeter-git", true, deps.VariantGit}, + } + for _, tc := range cases { + dep := NewBaseDistribution(nil).detectGreeterPackage(func(p string) bool { return p == tc.installed }) + if (dep.Status == deps.StatusInstalled) != tc.wantInstalled || dep.Variant != tc.wantVariant { + t.Errorf("%q installed: status %v variant %v, want installed=%v variant %v", tc.installed, dep.Status, dep.Variant, tc.wantInstalled, tc.wantVariant) + } + } +} + +func TestDetectCalendarPackageAcceptsOldNames(t *testing.T) { + for installed, want := range map[string]deps.PackageVariant{ + "": deps.VariantStable, + "dankcalendar-git": deps.VariantGit, + "dux-calendar-git": deps.VariantGit, + } { + dep := NewBaseDistribution(nil).detectCalendarPackage(func(p string) bool { return p == installed }) + if dep.Variant != want { + t.Errorf("%q installed: variant %v, want %v", installed, dep.Variant, want) + } + } +} + +func TestRenamedPackagesInstallNewNames(t *testing.T) { + cases := map[string]Distribution{ + "fedora": NewFedoraDistribution(Registry["fedora"], nil), + "opensuse": NewOpenSUSEDistribution(Registry["opensuse-tumbleweed"], nil), + "debian": NewDebianDistribution(Registry["debian"], nil), + "ubuntu": NewUbuntuDistribution(Registry["ubuntu"], nil), + "void": NewVoidDistribution(Registry["void"], nil), + } + for id, d := range cases { + for _, variant := range []deps.PackageVariant{deps.VariantStable, deps.VariantGit} { + packages := d.GetPackageMappingWithVariants(deps.WindowManagerNiri, map[string]deps.PackageVariant{"dms-greeter": variant, "dankcalendar": variant}) + for dep, want := range map[string]string{"dms-greeter": "dux-greeter", "danksearch": "dux-search", "dankcalendar": "dux-calendar"} { + if got := strings.TrimSuffix(packages[dep].Name, "-git"); got != want { + t.Errorf("%s %s (%v) installs %q, want %q", id, dep, variant, packages[dep].Name, want) + } + } + } + } +} diff --git a/core/internal/distros/debian.go b/core/internal/distros/debian.go index 3c919937d..f8159524f 100644 --- a/core/internal/distros/debian.go +++ b/core/internal/distros/debian.go @@ -62,7 +62,7 @@ func (d *DebianDistribution) DetectDependenciesWithTerminal(ctx context.Context, dependencies = append(dependencies, d.detectGit()) dependencies = append(dependencies, d.detectWindowManager(wm)) dependencies = append(dependencies, d.detectQuickshell()) - dependencies = append(dependencies, d.detectDMSGreeter()) + dependencies = append(dependencies, d.detectGreeterPackage(d.packageInstalled)) dependencies = append(dependencies, d.detectXDGPortal()) dependencies = append(dependencies, d.detectAccountsService()) @@ -77,7 +77,7 @@ func (d *DebianDistribution) DetectDependenciesWithTerminal(ctx context.Context, dependencies = append(dependencies, d.detectMatugen()) dependencies = append(dependencies, d.detectDanksearch()) - dependencies = append(dependencies, withGitVariant(d.detectDankCalendar(), d.packageInstalled("dankcalendar-git"))) + dependencies = append(dependencies, d.detectCalendarPackage(d.packageInstalled)) return dependencies, nil } @@ -94,12 +94,6 @@ func (d *DebianDistribution) detectAccountsService() deps.Dependency { return d.detectPackage("accountsservice", "D-Bus interface for user account query and manipulation", d.packageInstalled("accountsservice")) } -func (d *DebianDistribution) detectDMSGreeter() deps.Dependency { - gitInstalled := d.packageInstalled("dms-greeter-git") - dep := d.detectOptionalPackage("dms-greeter", "Dux Shell greetd greeter", gitInstalled || d.packageInstalled("dms-greeter")) - return withGitVariant(dep, gitInstalled) -} - func (d *DebianDistribution) packageInstalled(pkg string) bool { return debianPackageInstalledPrecisely(pkg) } @@ -159,11 +153,11 @@ func (d *DebianDistribution) GetPackageMappingWithVariants(wm deps.WindowManager // DMS packages from OBS with variant support "dms (Dux Shell)": d.getDmsMapping(variants["dms (Dux Shell)"]), "quickshell": d.getQuickshellMapping(variants["quickshell"]), - "dms-greeter": variantMapping("dms-greeter", variants["dms-greeter"], RepoTypeOBS, obsDeps), + "dms-greeter": variantMapping("dux-greeter", variants["dms-greeter"], RepoTypeOBS, obsDeps), "matugen": {Name: "matugen", Repository: RepoTypeOBS, RepoURL: obsDeps}, "ghostty": {Name: "ghostty", Repository: RepoTypeOBS, RepoURL: obsDeps}, - "danksearch": {Name: "danksearch", Repository: RepoTypeOBS, RepoURL: obsDeps}, - "dankcalendar": variantMapping("dankcalendar", variants["dankcalendar"], RepoTypeOBS, obsDeps), + "danksearch": {Name: "dux-search", Repository: RepoTypeOBS, RepoURL: obsDeps}, + "dankcalendar": variantMapping("dux-calendar", variants["dankcalendar"], RepoTypeOBS, obsDeps), } if wm == deps.WindowManagerNiri { diff --git a/core/internal/distros/fedora.go b/core/internal/distros/fedora.go index 17650ffe4..046bdeef7 100644 --- a/core/internal/distros/fedora.go +++ b/core/internal/distros/fedora.go @@ -2,10 +2,13 @@ package distros import ( "context" + "encoding/json" "fmt" "maps" "os/exec" + "slices" "strings" + "sync" "github.com/duxlabs-dev/dux-shell/core/internal/deps" "github.com/duxlabs-dev/dux-shell/core/internal/privesc" @@ -53,7 +56,8 @@ type FedoraDistribution struct { *ManualPackageInstaller config DistroConfig - rpmInstalled func(pkg string) bool + rpmInstalled func(pkg string) bool + ostreeRequested func() []string } func NewFedoraDistribution(config DistroConfig, logChan chan<- string) *FedoraDistribution { @@ -106,7 +110,7 @@ func (f *FedoraDistribution) DetectDependenciesWithTerminal(ctx context.Context, } dependencies = append(dependencies, wmDep) dependencies = append(dependencies, f.detectQuickshell()) - dependencies = append(dependencies, f.detectDMSGreeter()) + dependencies = append(dependencies, f.detectGreeterPackage(f.packageInstalled)) dependencies = append(dependencies, f.detectXDGPortal()) dependencies = append(dependencies, f.detectAccountsService()) @@ -128,7 +132,7 @@ func (f *FedoraDistribution) DetectDependenciesWithTerminal(ctx context.Context, dependencies = append(dependencies, f.detectMatugen()) dependencies = append(dependencies, f.detectDanksearch()) - dependencies = append(dependencies, withGitVariant(f.detectDankCalendar(), f.packageInstalled("dankcalendar-git"))) + dependencies = append(dependencies, f.detectCalendarPackage(f.packageInstalled)) return dependencies, nil } @@ -294,11 +298,11 @@ func (f *FedoraDistribution) GetPackageMappingWithVariants(wm deps.WindowManager // COPR packages "quickshell": f.getQuickshellMapping(variants["quickshell"]), - "dms-greeter": variantMapping("dms-greeter", variants["dms-greeter"], RepoTypeCOPR, coprDeps), + "dms-greeter": variantMapping("dux-greeter", variants["dms-greeter"], RepoTypeCOPR, coprDeps), "matugen": {Name: "matugen", Repository: RepoTypeCOPR, RepoURL: coprDeps}, "dms (Dux Shell)": f.getDmsMapping(variants["dms (Dux Shell)"]), - "danksearch": {Name: "danksearch", Repository: RepoTypeCOPR, RepoURL: coprDeps}, - "dankcalendar": variantMapping("dankcalendar", variants["dankcalendar"], RepoTypeCOPR, coprDeps), + "danksearch": {Name: "dux-search", Repository: RepoTypeCOPR, RepoURL: coprDeps}, + "dankcalendar": variantMapping("dux-calendar", variants["dankcalendar"], RepoTypeCOPR, coprDeps), } switch wm { @@ -381,12 +385,6 @@ func (f *FedoraDistribution) detectAccountsService() deps.Dependency { } } -func (f *FedoraDistribution) detectDMSGreeter() deps.Dependency { - gitInstalled := f.packageInstalled("dms-greeter-git") - dep := f.detectOptionalPackage("dms-greeter", "Dux Shell greetd greeter", gitInstalled || f.packageInstalled("dms-greeter")) - return withGitVariant(dep, gitInstalled) -} - func (f *FedoraDistribution) getPrerequisites() []string { return []string{ "dnf-plugins-core", @@ -829,6 +827,44 @@ func (f *FedoraDistribution) coprRepoFileCommand(repo string) string { coprRepoFile(repo), owner, project, chroot, owner, project, chroot) } +// rpm-ostree status runs over D-Bus and the layer view rebuilds its commands on every render. +var rpmOstreeRequested = sync.OnceValue(func() []string { + out, err := exec.Command("rpm-ostree", "status", "--json").Output() + if err != nil { + return nil + } + var status struct { + Deployments []struct { + Requested []string `json:"requested-packages"` + } `json:"deployments"` + } + if json.Unmarshal(out, &status) != nil || len(status.Deployments) == 0 { + return nil + } + return status.Deployments[0].Requested +}) + +func (f *FedoraDistribution) layeredRequests() map[string]bool { + list := f.ostreeRequested + if list == nil { + list = rpmOstreeRequested + } + requested := map[string]bool{} + for _, name := range list() { + requested[name] = true + } + return requested +} + +// layeredName is what --uninstall must name: the request, which may still be the pre-rename name +// even when its Provides already resolved to the renamed package. +func layeredName(requested map[string]bool, pkg string) string { + if old, ok := renamedPackages[pkg]; ok && !requested[pkg] && requested[old] { + return old + } + return pkg +} + // Shown to the user, never run. Terra-only packages have no rpm-ostree path. func (f *FedoraDistribution) LayerCommands(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags, disabledFlags map[string]bool) []string { var commands []string @@ -837,6 +873,7 @@ func (f *FedoraDistribution) LayerCommands(dependencies []deps.Dependency, wm de if skip == nil { skip = map[string]bool{} } + requested := f.layeredRequests() // rpm-ostree refuses to install what is already layered, so a reinstall mark only means a variant switch. for _, dep := range dependencies { if !reinstallFlags[dep.Name] || skip[dep.Name] || dep.Status != deps.StatusInstalled { @@ -847,7 +884,7 @@ func (f *FedoraDistribution) LayerCommands(dependencies []deps.Dependency, wm de skip[dep.Name] = true continue } - uninstall = append(uninstall, swap.From.Name) + uninstall = append(uninstall, layeredName(requested, swap.From.Name)) if swap.From.Name == swap.To.Name && swap.From.Repository == RepoTypeCOPR { var files []string for _, repo := range coprAliases(swap.From.RepoURL) { @@ -871,6 +908,17 @@ func (f *FedoraDistribution) LayerCommands(dependencies []deps.Dependency, wm de commands = append(commands, "echo priority=1 | sudo tee -a "+coprRepoFile("yalter/niri-git")) } } + // A request keeps its old name forever and resolves by name before Provides, ignoring Obsoletes. + for _, name := range slices.Sorted(maps.Keys(renamedPackages)) { + old := renamedPackages[name] + if !requested[old] || slices.Contains(uninstall, old) { + continue + } + uninstall = append(uninstall, old) + if !requested[name] && !slices.Contains(pkgs, name) { + pkgs = append(pkgs, name) + } + } for _, dep := range dependencies { if dep.Status == deps.StatusNeedsUpdate && !disabledFlags[dep.Name] { commands = append(commands, "sudo rpm-ostree upgrade") diff --git a/core/internal/distros/immutable_test.go b/core/internal/distros/immutable_test.go index 62e59ac5b..c7730e05f 100644 --- a/core/internal/distros/immutable_test.go +++ b/core/internal/distros/immutable_test.go @@ -137,6 +137,7 @@ func TestApplyImmutableDetection(t *testing.T) { func TestFedoraLayerCommands(t *testing.T) { f := NewFedoraDistribution(Registry["fedora"], nil) + f.ostreeRequested = func() []string { return nil } dependencies := []deps.Dependency{ {Name: "dms (Dux Shell)", Status: deps.StatusMissing}, {Name: "niri", Status: deps.StatusMissing, Variant: deps.VariantGit}, @@ -180,6 +181,7 @@ func TestFedoraLayerCommandsSwapVariants(t *testing.T) { f := NewFedoraDistribution(Registry["fedora"], nil) installed := map[string]bool{"dms-greeter": true, "dms": true} f.rpmInstalled = func(pkg string) bool { return installed[pkg] } + f.ostreeRequested = func() []string { return []string{"dms", "dms-greeter"} } orig := installedFromCOPR installedFromCOPR = func(pkg, repo string) bool { return pkg == "dms" && repo == "duxlabs/dms-git" } t.Cleanup(func() { installedFromCOPR = orig }) @@ -194,7 +196,7 @@ func TestFedoraLayerCommandsSwapVariants(t *testing.T) { for _, want := range []string{ "sudo rm -f /etc/yum.repos.d/_copr:copr.fedorainfracloud.org:duxlabs:dms-git.repo /etc/yum.repos.d/_copr:copr.fedorainfracloud.org:avengemedia:dms-git.repo", - "sudo rpm-ostree install dms dms-greeter-git --uninstall dms dms-greeter", + "sudo rpm-ostree install dms dux-greeter-git --uninstall dms dms-greeter", } { if !strings.Contains(got, want) { t.Fatalf("missing %q in:\n%s", want, got) @@ -205,6 +207,31 @@ func TestFedoraLayerCommandsSwapVariants(t *testing.T) { } } +func TestFedoraLayerCommandsRenamedRequests(t *testing.T) { + cases := []struct { + name string + rpm string + requested []string + want string + }{ + {"old package and request", "dms-greeter", []string{"dms-greeter", "danksearch"}, "sudo rpm-ostree install dux-greeter-git dux-search --uninstall dms-greeter danksearch"}, + {"old request resolved through Provides", "dux-greeter", []string{"dms-greeter"}, "sudo rpm-ostree install dux-greeter-git --uninstall dms-greeter"}, + {"new request", "dux-greeter", []string{"dux-greeter"}, "sudo rpm-ostree install dux-greeter-git --uninstall dux-greeter"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + f := NewFedoraDistribution(Registry["fedora"], nil) + f.rpmInstalled = func(pkg string) bool { return pkg == tc.rpm } + f.ostreeRequested = func() []string { return tc.requested } + dependencies := []deps.Dependency{{Name: "dms-greeter", Status: deps.StatusInstalled, Variant: deps.VariantGit, CanToggle: true}} + got := strings.Join(f.LayerCommands(dependencies, deps.WindowManagerNiri, map[string]bool{"dms-greeter": true}, nil), "\n") + if !strings.Contains(got, tc.want) { + t.Fatalf("missing %q in:\n%s", tc.want, got) + } + }) + } +} + func TestApplyImmutableDetectionReadsVariant(t *testing.T) { dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "dms"), []byte("#!/bin/sh\necho 'dms (git) v2.0.0-5333'\n"), 0o755); err != nil { diff --git a/core/internal/distros/opensuse.go b/core/internal/distros/opensuse.go index f86728057..a4a720ce7 100644 --- a/core/internal/distros/opensuse.go +++ b/core/internal/distros/opensuse.go @@ -75,7 +75,7 @@ func (o *OpenSUSEDistribution) DetectDependenciesWithTerminal(ctx context.Contex 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.detectGreeterPackage(o.packageInstalled)) dependencies = append(dependencies, o.detectXDGPortal()) dependencies = append(dependencies, o.detectAccountsService()) @@ -92,7 +92,7 @@ func (o *OpenSUSEDistribution) DetectDependenciesWithTerminal(ctx context.Contex dependencies = append(dependencies, o.detectMatugen()) dependencies = append(dependencies, o.detectDanksearch()) - dependencies = append(dependencies, withGitVariant(o.detectDankCalendar(), o.packageInstalled("dankcalendar-git"))) + dependencies = append(dependencies, o.detectCalendarPackage(o.packageInstalled)) return dependencies, nil } @@ -196,12 +196,6 @@ func obsZypperRepoURL(repo, dist string) string { return obsBaseURL(repo, dist) + "/" + repo + ".repo" } -func (o *OpenSUSEDistribution) detectDMSGreeter() deps.Dependency { - gitInstalled := o.packageInstalled("dms-greeter-git") - dep := o.detectOptionalPackage("dms-greeter", "Dux Shell greetd greeter", gitInstalled || o.packageInstalled("dms-greeter")) - return withGitVariant(dep, gitInstalled) -} - func (o *OpenSUSEDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping { return o.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant)) } @@ -218,11 +212,11 @@ func (o *OpenSUSEDistribution) GetPackageMappingWithVariants(wm deps.WindowManag // DMS packages from OBS "dms (Dux Shell)": o.getDmsMapping(variants["dms (Dux Shell)"]), "quickshell": o.getQuickshellMapping(variants["quickshell"]), - "dms-greeter": variantMapping("dms-greeter", variants["dms-greeter"], RepoTypeOBS, obsDeps), + "dms-greeter": variantMapping("dux-greeter", variants["dms-greeter"], RepoTypeOBS, obsDeps), "ghostty": {Name: "ghostty", Repository: RepoTypeOBS, RepoURL: obsDeps}, "matugen": {Name: "matugen", Repository: RepoTypeOBS, RepoURL: obsDeps}, - "danksearch": {Name: "danksearch", Repository: RepoTypeOBS, RepoURL: obsDeps}, - "dankcalendar": variantMapping("dankcalendar", variants["dankcalendar"], RepoTypeOBS, obsDeps), + "danksearch": {Name: "dux-search", Repository: RepoTypeOBS, RepoURL: obsDeps}, + "dankcalendar": variantMapping("dux-calendar", variants["dankcalendar"], RepoTypeOBS, obsDeps), } switch wm { diff --git a/core/internal/distros/ubuntu.go b/core/internal/distros/ubuntu.go index 4e7edf062..3d589a9ba 100644 --- a/core/internal/distros/ubuntu.go +++ b/core/internal/distros/ubuntu.go @@ -70,7 +70,7 @@ func (u *UbuntuDistribution) DetectDependenciesWithTerminal(ctx context.Context, dependencies = append(dependencies, u.detectGit()) dependencies = append(dependencies, u.detectWindowManager(wm)) dependencies = append(dependencies, u.detectQuickshell()) - dependencies = append(dependencies, u.detectDMSGreeter()) + dependencies = append(dependencies, u.detectGreeterPackage(u.packageInstalled)) dependencies = append(dependencies, u.detectXDGPortal()) dependencies = append(dependencies, u.detectAccountsService()) @@ -87,7 +87,7 @@ func (u *UbuntuDistribution) DetectDependenciesWithTerminal(ctx context.Context, dependencies = append(dependencies, u.detectMatugen()) dependencies = append(dependencies, u.detectDanksearch()) - dependencies = append(dependencies, withGitVariant(u.detectDankCalendar(), u.packageInstalled("dankcalendar-git"))) + dependencies = append(dependencies, u.detectCalendarPackage(u.packageInstalled)) return dependencies, nil } @@ -104,12 +104,6 @@ func (u *UbuntuDistribution) detectAccountsService() deps.Dependency { return u.detectPackage("accountsservice", "D-Bus interface for user account query and manipulation", u.packageInstalled("accountsservice")) } -func (u *UbuntuDistribution) detectDMSGreeter() deps.Dependency { - gitInstalled := u.packageInstalled("dms-greeter-git") - dep := u.detectOptionalPackage("dms-greeter", "Dux Shell greetd greeter", gitInstalled || u.packageInstalled("dms-greeter")) - return withGitVariant(dep, gitInstalled) -} - func (u *UbuntuDistribution) packageInstalled(pkg string) bool { return debianPackageInstalledPrecisely(pkg) } @@ -190,11 +184,11 @@ func (u *UbuntuDistribution) GetPackageMappingWithVariants(wm deps.WindowManager // DMS packages from PPAs "dms (Dux Shell)": u.getDmsMapping(variants["dms (Dux Shell)"]), "quickshell": u.getQuickshellMapping(variants["quickshell"]), - "dms-greeter": variantMapping("dms-greeter", variants["dms-greeter"], RepoTypePPA, ppaDeps), + "dms-greeter": variantMapping("dux-greeter", variants["dms-greeter"], RepoTypePPA, ppaDeps), "matugen": {Name: "matugen", Repository: RepoTypePPA, RepoURL: ppaDeps}, "ghostty": {Name: "ghostty", Repository: RepoTypePPA, RepoURL: ppaDeps}, - "danksearch": {Name: "danksearch", Repository: RepoTypePPA, RepoURL: ppaDeps}, - "dankcalendar": variantMapping("dankcalendar", variants["dankcalendar"], RepoTypePPA, ppaDeps), + "danksearch": {Name: "dux-search", Repository: RepoTypePPA, RepoURL: ppaDeps}, + "dankcalendar": variantMapping("dux-calendar", variants["dankcalendar"], RepoTypePPA, ppaDeps), } switch wm { diff --git a/core/internal/distros/variant_swap.go b/core/internal/distros/variant_swap.go index 8aa7c71c7..025fa1288 100644 --- a/core/internal/distros/variant_swap.go +++ b/core/internal/distros/variant_swap.go @@ -20,6 +20,7 @@ type VariantSwap struct { type VariantSwapper interface { InstalledVariant(stable, git PackageMapping) (deps.PackageVariant, bool) SwapVariants(ctx context.Context, swaps []VariantSwap, sudoPassword string, progressChan chan<- InstallProgressMsg) error + packageInstalled(pkg string) bool } type SwapStatus int @@ -53,18 +54,26 @@ func PlanVariantSwap(d Distribution, wm deps.WindowManager, dep string, target d case current == target: return VariantSwap{}, SwapAlreadyOnTarget case target == deps.VariantGit: - return VariantSwap{Dep: dep, From: stable, To: git}, SwapNeeded + return VariantSwap{Dep: dep, From: installedAs(swapper, stable), To: git}, SwapNeeded default: - return VariantSwap{Dep: dep, From: git, To: stable}, SwapNeeded + return VariantSwap{Dep: dep, From: installedAs(swapper, git), To: stable}, SwapNeeded } } +// installedAs names the package the remove step must target, which is the old name on hosts not yet upgraded past a rename. +func installedAs(swapper VariantSwapper, m PackageMapping) PackageMapping { + if name, ok := installedName(swapper.packageInstalled, m.Name); ok { + m.Name = name + } + return m +} + // installedVariantByName works when the two variants ship under different package names. func installedVariantByName(installed func(string) bool, stable, git PackageMapping) (deps.PackageVariant, bool) { - switch { - case installed(packageBaseName(git.Name)): + if _, ok := installedName(installed, packageBaseName(git.Name)); ok { return deps.VariantGit, true - case installed(packageBaseName(stable.Name)): + } + if _, ok := installedName(installed, packageBaseName(stable.Name)); ok { return deps.VariantStable, true } return deps.VariantStable, false diff --git a/core/internal/distros/variant_swap_test.go b/core/internal/distros/variant_swap_test.go index 805f648c0..9aa1638ff 100644 --- a/core/internal/distros/variant_swap_test.go +++ b/core/internal/distros/variant_swap_test.go @@ -15,13 +15,15 @@ type swapFake struct { func (f *swapFake) GetPackageMappingWithVariants(_ deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping { return map[string]PackageMapping{ - "dms-greeter": variantMapping("dms-greeter", variants["dms-greeter"], RepoTypeCOPR, "duxlabs/dux-repo"), + "dms-greeter": variantMapping("dux-greeter", variants["dms-greeter"], RepoTypeCOPR, "duxlabs/dux-repo"), "matugen": {Name: "matugen", Repository: RepoTypeSystem}, } } +func (f *swapFake) packageInstalled(pkg string) bool { return f.installed[pkg] } + func (f *swapFake) InstalledVariant(stable, git PackageMapping) (deps.PackageVariant, bool) { - return installedVariantByName(func(p string) bool { return f.installed[p] }, stable, git) + return installedVariantByName(f.packageInstalled, stable, git) } func (f *swapFake) SwapVariants(_ context.Context, swaps []VariantSwap, _ string, _ chan<- InstallProgressMsg) error { @@ -38,9 +40,12 @@ func TestPlanVariantSwap(t *testing.T) { want SwapStatus from, to string }{ - {"stable to git", "dms-greeter", "dms-greeter", deps.VariantGit, SwapNeeded, "dms-greeter", "dms-greeter-git"}, - {"git to stable", "dms-greeter", "dms-greeter-git", deps.VariantStable, SwapNeeded, "dms-greeter-git", "dms-greeter"}, - {"already on target", "dms-greeter", "dms-greeter-git", deps.VariantGit, SwapAlreadyOnTarget, "", ""}, + {"stable to git", "dms-greeter", "dux-greeter", deps.VariantGit, SwapNeeded, "dux-greeter", "dux-greeter-git"}, + {"git to stable", "dms-greeter", "dux-greeter-git", deps.VariantStable, SwapNeeded, "dux-greeter-git", "dux-greeter"}, + {"stable to git from the pre-rename package", "dms-greeter", "dms-greeter", deps.VariantGit, SwapNeeded, "dms-greeter", "dux-greeter-git"}, + {"git to stable from the pre-rename package", "dms-greeter", "dms-greeter-git", deps.VariantStable, SwapNeeded, "dms-greeter-git", "dux-greeter"}, + {"already on target", "dms-greeter", "dux-greeter-git", deps.VariantGit, SwapAlreadyOnTarget, "", ""}, + {"already on target under the pre-rename name", "dms-greeter", "dms-greeter-git", deps.VariantGit, SwapAlreadyOnTarget, "", ""}, {"not installed", "dms-greeter", "", deps.VariantGit, SwapNotInstalled, "", ""}, {"same mapping both ways", "matugen", "matugen", deps.VariantGit, SwapNoVariant, "", ""}, } @@ -56,7 +61,7 @@ func TestPlanVariantSwap(t *testing.T) { } func TestSwapReinstalledVariantsSkipsSwappedDeps(t *testing.T) { - d := &swapFake{installed: map[string]bool{"dms-greeter": true}} + d := &swapFake{installed: map[string]bool{"dux-greeter": true}} dependencies := []deps.Dependency{ {Name: "dms-greeter", Status: deps.StatusInstalled, Variant: deps.VariantGit}, {Name: "matugen", Status: deps.StatusInstalled}, @@ -68,8 +73,8 @@ func TestSwapReinstalledVariantsSkipsSwappedDeps(t *testing.T) { if err != nil { t.Fatal(err) } - if len(d.swapped) != 1 || d.swapped[0].To.Name != "dms-greeter-git" { - t.Fatalf("swapped = %+v, want only dms-greeter → dms-greeter-git", d.swapped) + if len(d.swapped) != 1 || d.swapped[0].To.Name != "dux-greeter-git" { + t.Fatalf("swapped = %+v, want only dux-greeter → dux-greeter-git", d.swapped) } if !skip["dms-greeter"] || skip["matugen"] { t.Fatalf("skip = %v, want the swapped greeter skipped and matugen reinstalled", skip) diff --git a/core/internal/distros/void.go b/core/internal/distros/void.go index 2de44175c..615123c2a 100644 --- a/core/internal/distros/void.go +++ b/core/internal/distros/void.go @@ -69,7 +69,7 @@ func (v *VoidDistribution) DetectDependenciesWithTerminal(ctx context.Context, w dependencies = append(dependencies, v.detectGit()) dependencies = append(dependencies, v.detectWindowManager(wm)) dependencies = append(dependencies, v.detectQuickshell()) - dependencies = append(dependencies, v.detectDMSGreeter()) + dependencies = append(dependencies, v.detectGreeterPackage(v.packageInstalled)) dependencies = append(dependencies, v.detectXDGPortal()) dependencies = append(dependencies, v.detectAccountsService()) dependencies = append(dependencies, v.detectDBus()) @@ -129,12 +129,6 @@ func (v *VoidDistribution) detectXDGPortal() deps.Dependency { return v.detectPackage("xdg-desktop-portal-gtk", "Desktop integration portal for GTK", v.packageInstalled("xdg-desktop-portal-gtk")) } -func (v *VoidDistribution) detectDMSGreeter() deps.Dependency { - gitInstalled := v.packageInstalled("dms-greeter-git") - dep := v.detectOptionalPackage("dms-greeter", "Dux Shell greetd greeter", gitInstalled || v.packageInstalled("dms-greeter")) - return withGitVariant(dep, gitInstalled) -} - func (v *VoidDistribution) detectAccountsService() deps.Dependency { return v.detectPackage("accountsservice", "D-Bus interface for user account query and manipulation", v.packageInstalled("accountsservice")) } @@ -318,9 +312,9 @@ func (v *VoidDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, "quickshell": {Name: "quickshell", Repository: RepoTypeSystem}, "matugen": {Name: "matugen", Repository: RepoTypeSystem}, "dms (Dux Shell)": v.getDmsMapping(variants["dms (Dux Shell)"]), - "dms-greeter": variantMapping("dms-greeter", variants["dms-greeter"], RepoTypeXBPS, VoidDepsRepo), - "danksearch": {Name: "danksearch", Repository: RepoTypeXBPS, RepoURL: VoidDepsRepo}, - "dankcalendar": {Name: "dankcalendar", Repository: RepoTypeXBPS, RepoURL: VoidDepsRepo}, + "dms-greeter": variantMapping("dux-greeter", variants["dms-greeter"], RepoTypeXBPS, VoidDepsRepo), + "danksearch": {Name: "dux-search", Repository: RepoTypeXBPS, RepoURL: VoidDepsRepo}, + "dankcalendar": {Name: "dux-calendar", Repository: RepoTypeXBPS, RepoURL: VoidDepsRepo}, } switch wm { diff --git a/core/internal/server/sysupdate/backend_dnf_test.go b/core/internal/server/sysupdate/backend_dnf_test.go index e7c5e3776..c20b728f8 100644 --- a/core/internal/server/sysupdate/backend_dnf_test.go +++ b/core/internal/server/sysupdate/backend_dnf_test.go @@ -43,12 +43,12 @@ bash.x86_64 5.2.40-1.fc41 updates`, }, { name: "one package offered by two repos is reported once", - input: `dms-greeter.x86_64 1.5.1-1.fc44 copr:copr.fedorainfracloud.org:avengemedia:danklinux -dms-greeter.x86_64 1.5.1-1.fc44 coprdep:copr.fedorainfracloud.org:avengemedia:danklinux`, + input: `dux-greeter.x86_64 1.5.1-1.fc44 copr:copr.fedorainfracloud.org:avengemedia:danklinux +dux-greeter.x86_64 1.5.1-1.fc44 coprdep:copr.fedorainfracloud.org:avengemedia:danklinux`, backendID: "dnf5", - installed: map[string]string{"dms-greeter": "1.5.0-1.fc44"}, + installed: map[string]string{"dux-greeter": "1.5.0-1.fc44"}, want: []Package{ - {Name: "dms-greeter.x86_64", Repo: RepoSystem, Backend: "dnf5", FromVersion: "1.5.0-1.fc44", ToVersion: "1.5.1-1.fc44"}, + {Name: "dux-greeter.x86_64", Repo: RepoSystem, Backend: "dnf5", FromVersion: "1.5.0-1.fc44", ToVersion: "1.5.1-1.fc44"}, }, }, { diff --git a/distro/scripts/ppa-build.sh b/distro/scripts/ppa-build.sh index 9f3d90e22..aef4a0aba 100755 --- a/distro/scripts/ppa-build.sh +++ b/distro/scripts/ppa-build.sh @@ -245,10 +245,10 @@ dms-git) dms) GIT_REPO="duxlabs-dev/dux-shell" ;; -danksearch) +dux-search) GIT_REPO="duxlabs-dev/dux-search" ;; -dgop) +dux-gop) GIT_REPO="duxlabs-dev/dux-gop" ;; esac @@ -540,8 +540,8 @@ fi # Handle packages that need pre-built binaries downloaded cd "$WORK_PACKAGE_DIR" case "$PACKAGE_NAME" in -danksearch) - info "Downloading pre-built binaries for danksearch..." +dux-search) + info "Downloading pre-built binaries for dux-search..." VERSION=$(dpkg-parsechangelog -S Version | sed 's/-[^-]*$//' | sed 's/ppa[0-9]*$//') if [ ! -f "dsearch-amd64" ]; then @@ -568,7 +568,7 @@ danksearch) fi fi ;; -dgop) +dux-gop) if [ ! -f "dgop" ]; then warn "dgop binary not found - should be committed to repo" fi diff --git a/distro/scripts/ppa-upload.sh b/distro/scripts/ppa-upload.sh index 0e12fbe0b..b98999038 100755 --- a/distro/scripts/ppa-upload.sh +++ b/distro/scripts/ppa-upload.sh @@ -452,7 +452,7 @@ if [ "$KEEP_BUILDS" = "false" ]; then done case "$PACKAGE_NAME" in - danksearch) + dux-search) if [ -f "$PACKAGE_DIR/dsearch-amd64" ]; then rm -f "$PACKAGE_DIR/dsearch-amd64" REMOVED=$((REMOVED + 1)) diff --git a/distro/void/README.md b/distro/void/README.md index 160944b0d..1e0107cfb 100644 --- a/distro/void/README.md +++ b/distro/void/README.md @@ -5,8 +5,8 @@ XBPS templates for Dux Shell on [Void Linux](https://voidlinux.org). | Package | Source repo | Template | | --- | --- | --- | | `dms` | Dux Shell | [`srcpkgs/dms/template`](srcpkgs/dms/template) | -| `dgop` | duxlabs-dev/dux-gop | maintained in the **dux-repo** repo (`distro/void/srcpkgs/dgop`) | -| `danksearch` | duxlabs-dev/dux-search | maintained in the **dux-repo** repo (`distro/void/srcpkgs/danksearch`) | +| `dux-gop` | duxlabs-dev/dux-gop | maintained in the **dux-repo** repo (`distro/void/srcpkgs/dux-gop`) | +| `dux-search` | duxlabs-dev/dux-search | maintained in the **dux-repo** repo (`distro/void/srcpkgs/dux-search`) | All build from source. @@ -18,7 +18,7 @@ This is a DMS maintained repo for VoidLinux until these packages are officially We serve both stable release and development packages from Cloudflare R2 at `void.duxlabs.dev`: `dms` and `dms-git` from `dms/current`, their dependencies -and `dms-greeter` from `dux-repo/current`. +and `dux-greeter` from `dux-repo/current`. > **Repository migration:** the former GitHub Pages repositories will be > frozen for 14 days at cutover. Their retirement date will be announced when @@ -59,14 +59,14 @@ checkout at `srcpkgs//template` to build or submit it. ## Dependencies -Installing `dms` automatically pulls in `quickshell`, `accountsservice`, `dgop`, +Installing `dms` automatically pulls in `quickshell`, `accountsservice`, `dux-gop`, `matugen` (which drives the Material You theming), `dbus`, `elogind`, and `mesa-dri` (GL drivers, required for compositors to render). The rest are optional, install whichever features you want: | Package | Enables | | --- | --- | -| `danksearch` | launcher / filesystem search | +| `dux-search` | launcher / filesystem search | | `cava` | audio visualiser widget | | `qt6-multimedia` | system sound feedback | | `qt6ct` | Qt app theming | @@ -81,16 +81,16 @@ The rest are optional, install whichever features you want: Inside a `void-packages` checkout (symlink or copy these `srcpkgs/` dirs in): ```sh -# build the dependency packages first (dms requires dgop) -./xbps-src pkg dgop -./xbps-src pkg danksearch +# build the dependency packages first (dms requires dux-gop) +./xbps-src pkg dux-gop +./xbps-src pkg dux-search ./xbps-src pkg dms # lint (xlint ships in the xtools package) xlint srcpkgs/dms/template # install the built packages -sudo xbps-install --repository=hostdir/binpkgs dms dgop +sudo xbps-install --repository=hostdir/binpkgs dms dux-gop ``` ## Running the shell diff --git a/quickshell/Modules/Settings/GreeterTab.qml b/quickshell/Modules/Settings/GreeterTab.qml index 540f5f74a..563ac3f94 100644 --- a/quickshell/Modules/Settings/GreeterTab.qml +++ b/quickshell/Modules/Settings/GreeterTab.qml @@ -223,7 +223,7 @@ Item { root.greeterStatusText = root.greeterStatusText + "\n\nstderr:\n" + err; return; } - var failure = I18n.tr("Failed to run '%1'. Ensure the %2 package is installed.", "greeter status error, %1 is a command, %2 a package name").arg("dms-greeter status").arg("dms-greeter") + " " + I18n.tr("(exit %1)", "process exit code").arg(exitCode); + var failure = I18n.tr("Failed to run '%1'. Ensure the %2 package is installed.", "greeter status error, %1 is a command, %2 a package name").arg("dms-greeter status").arg("dux-greeter") + " " + I18n.tr("(exit %1)", "process exit code").arg(exitCode); if (out !== "") failure = failure + "\n\n" + out; if (err !== "")