diff options
| -rw-r--r-- | CLAUDE.md | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..0b76b62 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,136 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this is + +Two deliverables: `nvchecker.toml` (a merged nvchecker config) and `nvtable` (a +Bash script that joins nvchecker's upstream results against the version +installed on this host and the version shipped by the SlackBuild repo that +packages it). No build step, no test framework, no dependencies to install. + +## Verifying changes + +There is no test suite. These are the checks that actually catch regressions: + +```bash +bash -n nvtable # syntax only +./nvtable -n -C # default view, no state writes, no color +./nvtable -n -C -a # every tracked package +``` + +**Always pass `-n` while developing.** Without it `nvtable` runs `nvtake`, +which rewrites `~/.config/nvchecker/old_ver.json` as a side effect of merely +displaying the table. + +To validate the config without touching live state, rewrite `[__config__]` to +point at scratch paths and run there: + +```bash +sed 's|^oldver.*|oldver = "/tmp/o.json"|; s|^newver.*|newver = "/tmp/n.json"|' \ + nvchecker.toml > /tmp/run.toml +echo '{"version":2,"data":{}}' > /tmp/o.json +nvchecker -c /tmp/run.toml 2>&1 | grep -oE '\] [^:]+: no-result' +``` + +A stanza that resolves prints `updated to <version>`; a broken one prints +`no-result`. The strongest check on a stanza is that the version it returns +equals the `VERSION=` in that package's `.info` — that caught a stanza pointing +at an entirely wrong upstream project (`mutagen-io/mutagen` vs +`quodlibet/mutagen`) that still resolved cleanly. + +`nvtable` reads `new_ver.json` and never refreshes it. After renaming stanzas +the table reports the old names as untracked until `nvchecker` runs again. + +## Live state this touches + +The repo is self-contained, but the installed tool reaches outside it: + +| Path | Role | +|------|------| +| `~/.config/nvchecker/nvchecker.toml` | the deployed copy of this config | +| `~/.config/nvchecker/{old,new}_ver.json` | nvchecker state; `nvtake` writes `old_ver` | +| `~/bin/nvtable` | symlink to the script in this repo | +| `~/.profile.d/qar.sh` | login script; calls `nvtable` where `nvcmp` used to run | + +Because `qar.sh` runs on every interactive shell, a bug in `nvtable` shows up +in every new terminal. Test with `-n -C` before touching the deployed copy. + +`NVTABLE_CONF` and `NVTABLE_NEWVER` override the config and state paths, which +is how to exercise the real script against scratch data. + +## Architecture + +`nvtable` is one pass with three joins. The subtlety is entirely in the joins, +not the control flow. + +**Name resolution.** The design rule is that a stanza name equals the Slackware +package name, so most rows join with no mapping at all. Two alias maps cover +the rest, and they are separate because the mappings differ per direction: +`ALIAS` maps a stanza to installed package name(s) (`kvantum` → +`kvantum-qt5 kvantum-qt6`, one-to-many), while `REPO_ALIAS` maps a stanza to a +SlackBuild directory (`kitty` → `kitty-bin`). Adding one map for both breaks +the other direction. + +**Installed version** resolves through three sources in order: +`/var/log/packages`, then the appimage-updater state file, then `flatpak list`. + +**Repo version** is the `VERSION=` line from +`<repo>/<category>/<pkg>/<pkg>.info`. Note the category level: `.info` files sit +at depth 3, not 2. The repositories scanned are the `REPOS` array at the top of +the script. + +### Traps that produced real bugs here + +These are non-obvious and each one silently produced wrong output: + +- **A Slackware package filename cannot be parsed by counting dashes.** It is + `PRGNAM-VERSION-ARCH-BUILD`, but VERSION may itself contain a dash + (`solvespace-3.1-659215d`). Lookups anchor on the package name being searched + for, and require the following character to be a digit so `kvantum` does not + match `kvantum-qt5`. +- **Installed versions carry build metadata upstream never has** (`r8125` is + `9.018.00_6.18.41`, version plus kernel). `vers_equal` treats installed as + equal when it merely extends upstream at a component boundary; strict equality + marks such packages permanently behind. +- **`read` with `IFS=$'\t'` collapses consecutive tabs**, so an empty field + shifts every later field left. Row records are packed with `|` for this + reason. Do not "simplify" it back to tabs. +- **Installed-behind and repo-behind are independent conditions.** A row can be + both (🔴🟡). Collapsing the marker to a single worst-case state hides the + packaging work. +- **Column padding must happen before color codes are added**, or `printf` + counts escape bytes as width. + +### nvtake + +When a package is caught up, its name is collected and one `nvtake` call +records them all, moving `new_ver` → `old_ver` so nvchecker's state tracks this +system rather than only upstream history. `-n` disables it. + +The call is wrapped in `flock -n` because `nvtake` does read-modify-write on +`old_ver.json` with no locking of its own, and `qar.sh` runs on every shell — so +several terminals opening at once means concurrent writers. On lock contention +the take is skipped rather than queued; the next run recomputes the same set. + +## nvchecker.toml conventions + +- **Stanza name = Slackware package name** wherever a package exists. Renaming a + stanza silently orphans its history in `old_ver.json` (the old key stays, + nothing matches the new one) and breaks any external consumer that looks + entries up by name — `appimage-updater` reads this config and requires the + entries `Feishin` and `cherrytree` verbatim. +- **`# repo=` comments** mark which repository ships each package (`sbo`, `my`, + `sps`); entries with none are tracked-only. +- **A name containing a dot must be quoted.** `[llama.cpp-vulkan]` parses as a + nested `llama` table and fails with `no source specified`. +- **`use_latest_release` on GitHub needs `prefix = "v"`** when the tags carry + one, otherwise the version comes back as `v8.30.1` and never matches a `.info` + VERSION. +- Prefer `use_latest_release` over `use_max_tag`: `use_max_tag` hits a tightly + rate-limited endpoint. +- Per-repo `.extras/nvchecker.toml` files are deliberately kept in their own + repositories for standalone sweeps. This config is the superset; they drift. + +The GitHub token lives in `~/.config/nvchecker/keys.toml`, which `.gitignore` +excludes so it cannot be committed alongside the config. |
