Feature 57 (config and provisioning): modules declare config and provisions, their files rendered from templates; synced with main (issues of feature 57, 874)
This commit is contained in:
@@ -15,10 +15,10 @@ or reads `/proc`. It is for the two workstations only.
|
||||
|
||||
| path | class (ADR 0182) | from |
|
||||
|---|---|---|
|
||||
| `~/.config/user-dirs.dirs` | owned, the found file kept once | [`config/user-dirs.dirs`](config/user-dirs.dirs), three folders from settings |
|
||||
| `~/.config/user-dirs.locale` | owned, the found file kept once | [`config/user-dirs.locale`](config/user-dirs.locale) |
|
||||
| `~/.config/user-dirs.conf` | owned, new | [`config/user-dirs.conf`](config/user-dirs.conf): `enabled=False` |
|
||||
| `/etc/xdg/mimeapps.list` | owned, new | [`config/mimeapps.list`](config/mimeapps.list): the machine's default applications |
|
||||
| `~/.config/user-dirs.dirs` | owned, the found file kept once | [`files/user-dirs.dirs`](files/user-dirs.dirs), three folders from settings |
|
||||
| `~/.config/user-dirs.locale` | owned, the found file kept once | [`files/user-dirs.locale`](files/user-dirs.locale) |
|
||||
| `~/.config/user-dirs.conf` | owned, new | [`files/user-dirs.conf`](files/user-dirs.conf): `enabled=False` |
|
||||
| `/etc/xdg/mimeapps.list` | owned, new | [`files/mimeapps.list`](files/mimeapps.list): the machine's default applications |
|
||||
|
||||
## What it does not own, and why
|
||||
|
||||
|
||||
@@ -55,16 +55,16 @@ func app(name, exec, mime string) string {
|
||||
|
||||
// The desktop's folders as found on 2026-10-05, in the module's file with that machine's settings.
|
||||
func desktopUserDirs(t *testing.T) string {
|
||||
raw, err := os.ReadFile(filepath.Join("..", "..", "config", "user-dirs.dirs"))
|
||||
raw, err := os.ReadFile(filepath.Join("..", "..", "files", "user-dirs.dirs"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := strings.NewReplacer("${setting:desktop-dir}", "$HOME/", "${setting:publicshare-dir}", "$HOME/", "${setting:videos-dir}", "$HOME/")
|
||||
r := strings.NewReplacer("${config:desktop-dir}", "$HOME/", "${config:publicshare-dir}", "$HOME/", "${config:videos-dir}", "$HOME/")
|
||||
return r.Replace(string(raw))
|
||||
}
|
||||
|
||||
func TestTheFoldersAreReadAsXdgUserDirResolvesThem(t *testing.T) {
|
||||
conf, _ := os.ReadFile(filepath.Join("..", "..", "config", "user-dirs.conf"))
|
||||
conf, _ := os.ReadFile(filepath.Join("..", "..", "files", "user-dirs.conf"))
|
||||
m := fake(t, map[string]string{
|
||||
home + "/.config/user-dirs.dirs": desktopUserDirs(t),
|
||||
home + "/.config/user-dirs.conf": string(conf),
|
||||
@@ -159,7 +159,7 @@ func TestADuplicateGroupOrKeyIsReportedNotMerged(t *testing.T) {
|
||||
|
||||
// A machine with the mesh's list, the person's list, and some applications.
|
||||
func mimeMachine(t *testing.T, extra map[string]string) *Machine {
|
||||
mesh, err := os.ReadFile(filepath.Join("..", "..", "config", "mimeapps.list"))
|
||||
mesh, err := os.ReadFile(filepath.Join("..", "..", "files", "mimeapps.list"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -74,9 +74,10 @@ func TestItOwnsTheFilesItsSourcesHold(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
files++
|
||||
src, _ := os.ReadFile(filepath.Join("..", "..", "config", w[2]))
|
||||
if r["path"] != w[0] || r["mode"] != w[1] || r["content"] != string(src) || r["into"] != nil {
|
||||
t.Errorf("%s is not config/%s, whole, at %s mode %s: %v", id, w[2], w[0], w[1], r["path"])
|
||||
_, inline := r["content"]
|
||||
if _, err := os.Stat(filepath.Join("..", "..", "files", w[2])); err != nil || inline ||
|
||||
r["template"] != "files/"+w[2] || r["path"] != w[0] || r["mode"] != w[1] || r["into"] != nil {
|
||||
t.Errorf("%s is not files/%s, whole, as its template, at %s mode %s: %v", id, w[2], w[0], w[1], r["path"])
|
||||
}
|
||||
home := strings.HasPrefix(w[0], "${machine:account-home}/")
|
||||
if home != (r["owner"] == "${machine:account}") {
|
||||
@@ -100,9 +101,9 @@ func TestItOwnsTheFilesItsSourcesHold(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTheFoldersFileIsXdgUserDirsOwnFormatWithThreeSettings(t *testing.T) {
|
||||
raw, _ := os.ReadFile(filepath.Join("..", "..", "config", "user-dirs.dirs"))
|
||||
raw, _ := os.ReadFile(filepath.Join("..", "..", "files", "user-dirs.dirs"))
|
||||
text := string(raw)
|
||||
settings := regexp.MustCompile(`\$\{setting:([a-z0-9-]+)\}`).FindAllStringSubmatch(text, -1)
|
||||
settings := regexp.MustCompile(`\$\{config:([a-z0-9-]+)\}`).FindAllStringSubmatch(text, -1)
|
||||
var names []string
|
||||
for _, s := range settings {
|
||||
names = append(names, s[1])
|
||||
@@ -128,14 +129,14 @@ func TestTheFoldersFileIsXdgUserDirsOwnFormatWithThreeSettings(t *testing.T) {
|
||||
if !strings.Contains(firstLines(text, 3), meshMark) {
|
||||
t.Error("the header xdg_check recognises the file by")
|
||||
}
|
||||
conf, _ := os.ReadFile(filepath.Join("..", "..", "config", "user-dirs.conf"))
|
||||
conf, _ := os.ReadFile(filepath.Join("..", "..", "files", "user-dirs.conf"))
|
||||
if !regexp.MustCompile(`(?m)^enabled=False$`).Match(conf) {
|
||||
t.Error("xdg-user-dirs-update must be disabled, or it rewrites the mesh's file at login")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTheMeshsDefaultsNameEachTypeOnceInOneGroupEach(t *testing.T) {
|
||||
raw, _ := os.ReadFile(filepath.Join("..", "..", "config", "mimeapps.list"))
|
||||
raw, _ := os.ReadFile(filepath.Join("..", "..", "files", "mimeapps.list"))
|
||||
k := ParseKeyFile(string(raw))
|
||||
if len(k.DupGroups)+len(k.DupKeys)+len(k.Unparsable) != 0 {
|
||||
t.Fatalf("%+v", k)
|
||||
@@ -212,3 +213,31 @@ func TestTheFoldersFileStaysTrustedUntilSettingsAreQuoted(t *testing.T) {
|
||||
}
|
||||
t.Fatal("no user-dirs resource")
|
||||
}
|
||||
|
||||
// No file resource carries its text inline: each names its template in files/, and the build packs files/
|
||||
// into the one archive the controller reads the templates back from (novox/hq ADR 0349 §2).
|
||||
func TestEveryFileTakesItsTextFromATemplate(t *testing.T) {
|
||||
m, _ := readManifest(t)
|
||||
for _, r := range m.Resources {
|
||||
if _, inline := r["content"]; inline {
|
||||
t.Errorf("resource %v keeps its text inline: keep it in files/ and name it as the template", r["id"])
|
||||
}
|
||||
if name, ok := r["template"].(string); ok {
|
||||
if _, err := os.Stat(filepath.Join("..", "..", name)); err != nil {
|
||||
t.Errorf("resource %v names %s: %v", r["id"], name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
packs := 0
|
||||
for _, a := range m.Build.Artifacts {
|
||||
if a["kind"] == "archive" && a["from"] == "files" {
|
||||
packs++
|
||||
if a["name"] != "files" {
|
||||
t.Errorf("the templates archive is named %v, not files", a["name"])
|
||||
}
|
||||
}
|
||||
}
|
||||
if packs != 1 {
|
||||
t.Errorf("the build packs files/ into %d archives, not one", packs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
# xdg-user-dirs-update never rewrites this file: user-dirs.conf beside it says enabled=False. Each
|
||||
# line is xdg-user-dirs' only format, XDG_xxx_DIR="$HOME/yyy" or XDG_xxx_DIR="/yyy". "$HOME/" is
|
||||
# the home itself: the folder is not wanted. xdg_user_dirs says what each one resolves to.
|
||||
XDG_DESKTOP_DIR="${setting:desktop-dir}"
|
||||
XDG_DESKTOP_DIR="${config:desktop-dir}"
|
||||
XDG_DOWNLOAD_DIR="$HOME/Downloads"
|
||||
XDG_TEMPLATES_DIR="$HOME/Templates"
|
||||
XDG_PUBLICSHARE_DIR="${setting:publicshare-dir}"
|
||||
XDG_PUBLICSHARE_DIR="${config:publicshare-dir}"
|
||||
XDG_DOCUMENTS_DIR="$HOME/Documents"
|
||||
XDG_MUSIC_DIR="$HOME/Music"
|
||||
XDG_PICTURES_DIR="$HOME/Pictures"
|
||||
XDG_VIDEOS_DIR="${setting:videos-dir}"
|
||||
XDG_VIDEOS_DIR="${config:videos-dir}"
|
||||
XDG_PROJECTS_DIR="$HOME/projects"
|
||||
+29
-4
@@ -11,6 +11,26 @@
|
||||
"xdg_autostart",
|
||||
"xdg_check"
|
||||
],
|
||||
"config": {
|
||||
"desktop-dir": {
|
||||
"type": "string",
|
||||
"description": "The desktop folder, as user-dirs.dirs writes it: \"$HOME/<folder>\", or \"$HOME/\" when the folder is not wanted; it differs between the workstations, so each says its own.",
|
||||
"trusted": true,
|
||||
"required": true
|
||||
},
|
||||
"publicshare-dir": {
|
||||
"type": "string",
|
||||
"description": "The public share folder, as user-dirs.dirs writes it: \"$HOME/<folder>\", or \"$HOME/\" when the folder is not wanted; it differs between the workstations, so each says its own.",
|
||||
"trusted": true,
|
||||
"required": true
|
||||
},
|
||||
"videos-dir": {
|
||||
"type": "string",
|
||||
"description": "The videos folder, as user-dirs.dirs writes it: \"$HOME/<folder>\", or \"$HOME/\" when the folder is not wanted; it differs between the workstations, so each says its own.",
|
||||
"trusted": true,
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"id": "package-xdg-user-dirs",
|
||||
@@ -28,7 +48,7 @@
|
||||
"path": "${machine:account-home}/.config/user-dirs.dirs",
|
||||
"owner": "${machine:account}",
|
||||
"mode": "0600",
|
||||
"content": "# The account's folders (module xdg, novox/hq ADR 0182), written by the mesh and replaced at every\n# push. Adopted from the workstations' own files of 2026-10-05. A folder that differs between the\n# machines is a setting of this module; every other one is the same everywhere. PROJECTS names the\n# folder the operator's repositories are in (it pointed at the home, or was absent).\n#\n# xdg-user-dirs-update never rewrites this file: user-dirs.conf beside it says enabled=False. Each\n# line is xdg-user-dirs' only format, XDG_xxx_DIR=\"$HOME/yyy\" or XDG_xxx_DIR=\"/yyy\". \"$HOME/\" is\n# the home itself: the folder is not wanted. xdg_user_dirs says what each one resolves to.\nXDG_DESKTOP_DIR=\"${setting:desktop-dir}\"\nXDG_DOWNLOAD_DIR=\"$HOME/Downloads\"\nXDG_TEMPLATES_DIR=\"$HOME/Templates\"\nXDG_PUBLICSHARE_DIR=\"${setting:publicshare-dir}\"\nXDG_DOCUMENTS_DIR=\"$HOME/Documents\"\nXDG_MUSIC_DIR=\"$HOME/Music\"\nXDG_PICTURES_DIR=\"$HOME/Pictures\"\nXDG_VIDEOS_DIR=\"${setting:videos-dir}\"\nXDG_PROJECTS_DIR=\"$HOME/projects\"\n"
|
||||
"template": "files/user-dirs.dirs"
|
||||
},
|
||||
{
|
||||
"id": "user-dirs-locale",
|
||||
@@ -36,7 +56,7 @@
|
||||
"path": "${machine:account-home}/.config/user-dirs.locale",
|
||||
"owner": "${machine:account}",
|
||||
"mode": "0644",
|
||||
"content": "en_US\n"
|
||||
"template": "files/user-dirs.locale"
|
||||
},
|
||||
{
|
||||
"id": "user-dirs-update",
|
||||
@@ -44,14 +64,14 @@
|
||||
"path": "${machine:account-home}/.config/user-dirs.conf",
|
||||
"owner": "${machine:account}",
|
||||
"mode": "0644",
|
||||
"content": "# xdg-user-dirs-update for this account (module xdg, novox/hq ADR 0182), written by the mesh and\n# replaced at every push.\n#\n# The update runs at every login (its XDG autostart entry, which the session's dex starts). Enabled,\n# it reassigns a folder it cannot find to the home and adds a line for every folder its defaults\n# know, rewriting user-dirs.dirs, which is the mesh's. Disabled, it does nothing, even with --force.\n# The folders are this module's to say.\nenabled=False\nfilename_encoding=UTF-8\n"
|
||||
"template": "files/user-dirs.conf"
|
||||
},
|
||||
{
|
||||
"id": "default-applications",
|
||||
"type": "file",
|
||||
"path": "/etc/xdg/mimeapps.list",
|
||||
"mode": "0644",
|
||||
"content": "# The machine's default applications (module xdg, novox/hq ADR 0182), written by the mesh and\n# replaced at every push.\n#\n# This is the lowest list an application consults. The account's own ~/.config/mimeapps.list comes\n# first: every program that offers \"always open with\" writes there, and so does xdg_default. That\n# file is the person's and is never written by the mesh, so a choice made there always wins; this\n# list answers only what the person has not chosen. Adopted from the workstations' own lists of\n# 2026-10-05, without the entries naming an application neither workstation has installed (see the\n# module's README).\n[Default Applications]\napplication/pdf=firefox.desktop;\napplication/x-extension-htm=firefox.desktop;\napplication/x-extension-shtml=firefox.desktop;\napplication/x-extension-xht=firefox.desktop;\napplication/x-extension-xhtml=firefox.desktop;\napplication/xhtml+xml=firefox.desktop;\ntext/html=firefox.desktop;\nx-scheme-handler/chrome=firefox.desktop;\nx-scheme-handler/ftp=firefox.desktop;\nx-scheme-handler/http=firefox.desktop;\nx-scheme-handler/https=firefox.desktop;\ntext/markdown=org.kde.ghostwriter.desktop;\ninode/directory=thunar.desktop;\nx-scheme-handler/abc=plexamp.desktop;\nx-scheme-handler/slack=slack.desktop;\nx-scheme-handler/tg=org.telegram.desktop.desktop;\n\n[Added Associations]\nvideo/mp4=mpv.desktop;mplayer.desktop;vlc.desktop;\n"
|
||||
"template": "files/mimeapps.list"
|
||||
}
|
||||
],
|
||||
"build": {
|
||||
@@ -66,6 +86,11 @@
|
||||
"loads": [
|
||||
"xdg-tools"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "files",
|
||||
"kind": "archive",
|
||||
"from": "files"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user