Files
mesh-catalog/modules/xdg/cmd/xdg-tools/check.go
T
jochen 785d32407b xdg: own the account's folders and the machine's default applications
The workstations' XDG conventions had no owner: xdg-user-dirs-update rewrote
the folders file at every login, both machines' default-application lists
named an editor neither has, and the laptop kept two dead links of the
retired predecessor. The module adopts the operator's folders (three as
settings, as the machines differ), disables the update so it cannot undo the
mesh's file, and writes the machine's own mimeapps list, the last one read,
so the person's choices in their own list always win. Autostart is listed,
not owned: each entry is its application's module's.
2026-10-05 11:50:34 +02:00

127 lines
4.5 KiB
Go

package main
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
)
// Check is one line of xdg_check's answer.
type Check struct {
Name string `json:"name"`
OK bool `json:"ok"`
Detail string `json:"detail"`
}
// Check is xdg_check: whether the account's XDG conventions are as the module declares them, and
// what is wrong in what it holds as found.
func (m *Machine) Check(env string) map[string]any {
var checks []Check
add := func(name string, ok bool, detail string) { checks = append(checks, Check{name, ok, detail}) }
// The folders.
u := m.UserDirs()
add("user-dirs.dirs is the mesh's", u.Meshs, map[bool]string{
true: u.File + " carries the module's header",
false: u.File + " has no header of module xdg: the module is not assigned here, or the file was rewritten since the last push",
}[u.Meshs])
var missing []string
for _, d := range u.Dirs {
if !d.Home && !d.Exists {
missing = append(missing, fmt.Sprintf("%s → %s (%s)", d.Name, d.Path, d.Kind))
}
}
add("every folder it names exists", len(missing) == 0, orNone(missing, "each is a directory, or the home"))
add("xdg-user-dirs-update leaves it alone", !u.Update.Enabled, "enabled is "+fmt.Sprint(u.Update.Enabled)+", from "+u.Update.DecidedBy)
// The default applications.
var listProblems []string
meshListFound := false
for _, l := range m.lists() {
if l.File == MeshList {
meshListFound = true
}
switch {
case l.Broken != "":
listProblems = append(listProblems, l.File+": "+l.Broken)
case len(l.Keys.DupGroups) > 0:
listProblems = append(listProblems, fmt.Sprintf("%s: the group %s is written twice, and GLib and xdg-mime then answer differently", l.File, strings.Join(l.Keys.DupGroups, ", ")))
case len(l.Keys.DupKeys) > 0:
listProblems = append(listProblems, fmt.Sprintf("%s: %s written twice, and GLib and xdg-mime then answer differently", l.File, strings.Join(l.Keys.DupKeys, ", ")))
case len(l.Keys.Unparsable) > 0:
listProblems = append(listProblems, fmt.Sprintf("%s: lines %v are not key=value under a group", l.File, l.Keys.Unparsable))
}
if m.deprecated(l.File) && strings.HasPrefix(l.File, "~/") {
listProblems = append(listProblems, l.File+": a list in the deprecated place, which nothing current writes (a predecessor's; remove it once)")
}
}
for _, f := range []string{filepath.Join(m.dataHome(), "applications", "defaults.list")} {
if m.kind(f) != "absent" || m.isLink(f) {
listProblems = append(listProblems, m.tilde(f)+": the long-obsolete defaults.list, which nothing reads any more; remove it once")
}
}
add("the mesh's default applications are in place", meshListFound, MeshList)
add("the mimeapps lists are sane", len(listProblems) == 0, orNone(listProblems, "each parses, with no group or key written twice"))
var dead []string
for _, d := range m.deadDefaults() {
dead = append(dead, fmt.Sprintf("%s: %s=%s", d.File, d.Type, d.ID))
}
add("every default names an installed application", len(dead) == 0, orNone(dead, "all installed"))
var broken []string
for _, e := range m.brokenHandlers() {
why := e.Broken
if why == "" {
why = e.Program + " is not installed"
}
broken = append(broken, fmt.Sprintf("%s (%s): %s", e.ID, e.File, why))
}
add("no handler's program is missing", len(broken) == 0, orNone(broken, "every application declaring a type can run"))
// Autostart.
starts := m.Autostarts(env)
byProgram := map[string][]string{}
var twice, failing []string
for _, a := range starts {
if !a.DexStarts {
continue
}
byProgram[filepath.Base(a.Program)] = append(byProgram[filepath.Base(a.Program)], a.File)
if a.AlsoStarted != "" {
twice = append(twice, fmt.Sprintf("%s: by %s and by %s", a.Program, a.File, a.AlsoStarted))
}
if !a.Found {
failing = append(failing, fmt.Sprintf("%s: %s is not installed", a.File, a.Program))
}
}
for _, p := range sortedKeys(byProgram) {
if len(byProgram[p]) > 1 {
twice = append(twice, fmt.Sprintf("%s: by %s", p, strings.Join(byProgram[p], " and ")))
}
}
sort.Strings(twice)
add("no program is started twice at login", len(twice) == 0, orNone(twice, "each autostarted program has one starter"))
add("every program dex starts is installed", len(failing) == 0, orNone(failing, "all installed"))
failingN := 0
for _, c := range checks {
if !c.OK {
failingN++
}
}
return map[string]any{"checks": checks, "failing": failingN}
}
func (m *Machine) isLink(path string) bool {
_, err := os.Lstat(m.p(path))
return err == nil
}
func orNone(items []string, fine string) string {
if len(items) == 0 {
return fine
}
return strings.Join(items, "; ")
}