# danix-nvchecker

A unified [nvchecker](https://github.com/lilydjwg/nvchecker) configuration plus
`nvtable`, a small Bash tool that answers the question nvchecker alone cannot:

> upstream released a new version, but **do I have it installed**, and does the
> SlackBuild in my repo still ship the old one?

`nvcmp` compares upstream against its own last-seen state. `nvtable` adds the
two columns that matter in practice, the version installed on this machine and
the version shipped by the SlackBuild repo that packages it.

```
     package                 upstream       installed      repo
  🔴   discord                 1.0.152        1.0.151        my 1.0.152
  🔴🟡 nuclei                  3.11.1         3.11.0         sbo 3.11.0
  🔴   signal-desktop          8.22.0         8.20.0         --
  🟢   qarma                   1.1.1          1.1.1          my 1.1.1

  26 to upgrade · 2 repos to bump · 32 current · 13 not installed
```

| marker | meaning |
|--------|---------|
| 🔴 | installed version is behind upstream |
| 🟡 | the repo `.info` still ships an older version, so it needs a bump |
| 🟢 | installed and packaged versions both match upstream |
| ⚪ | not installed on this host (tracked only) |

A row can carry two markers: 🔴🟡 means upstream moved and neither the system
nor the SlackBuild has caught up.

## Contents

- `nvchecker.toml` - one config covering everything tracked: software installed
  on this machine, packages maintained in three SlackBuild repos, and services
  running elsewhere.
- `nvtable` - the comparison tool.

## nvchecker.toml

Merged from a personal config and the `.extras/nvchecker.toml` of three
SlackBuild repositories, deduplicated. The per-repo configs are kept in their
repos for standalone sweeps; this one is the superset used day to day.

Two conventions make the joins work without a lookup table:

- **The stanza name is the Slackware package name** wherever a package exists,
  so results join directly against `/var/log/packages` and against
  `<pkg>/<pkg>.info`. Where they genuinely differ (`kvantum` builds both
  `kvantum-qt5` and `kvantum-qt6`) `nvtable` carries a small alias map.
- **A `# repo=` comment marks which repository ships the package**: `sbo`,
  `my`, `sps`, or nothing for tracked-only entries.

Watch for two nvchecker details that cause silent wrong answers:

- A stanza name containing a dot **must be quoted**. `[llama.cpp-vulkan]` parses
  as a nested `llama` table and fails with `no source specified`; write
  `["llama.cpp-vulkan"]`.
- Omitting `prefix = "v"` on a `use_latest_release` GitHub source yields
  `v8.30.1` rather than `8.30.1`, which then never matches a `.info` VERSION.

## nvtable

### Requirements

`bash`, `jq`, and nvchecker's `nvcmp`/`nvtake`. `flatpak` is optional and used
only if present.

### Usage

```
nvtable [-a] [-n] [-C] [-E]

  -a, --all       show every tracked package, not just those needing action
  -n, --no-take   do not run nvtake on caught-up packages
  -C, --no-color  plain output
  -E, --no-emoji  ASCII status markers instead of emoji
```

By default only actionable rows print. Packages that are current, and packages
not installed on this host, are folded into the summary line. Color is disabled
automatically when output is not a terminal.

### Where the three versions come from

**upstream** is read from nvchecker's `new_ver.json`. `nvtable` never fetches
anything itself, so it is fast enough to run on every login; refreshing that
file is nvchecker's job.

**installed** is resolved from four sources, in order:

1. the `OPT_JSON` map, for apps unpacked under `/opt` that are none of the
   below and carry their version in a JSON file (Electron apps such as Typora).
   Each entry is a stanza name mapped to `<path>:<jq filter>`.
2. `/var/log/packages` for Slackware packages
3. `~/.local/state/appimage-updater/installed.json` for AppImages managed by
   [appimage-updater](https://git.danix.xyz/appimage-updater/)
4. `flatpak list` for flatpak apps

**repo** is the `VERSION=` line of `<repo>/<category>/<pkg>/<pkg>.info` across
the configured SlackBuild repositories.

### Version comparison

Versions arrive from unrelated sources and rarely agree on formatting, so they
are normalised before comparison: a leading `v` is stripped, `-` and `_` fold to
`.`, and case is ignored.

Installed versions also carry build metadata that upstream never has, for
instance `r8125` installs as `9.018.00_6.18.41` (version plus kernel) against an
upstream `9.018.00`. An installed version that merely *extends* upstream at a
component boundary counts as equal, otherwise every such package would report as
permanently behind.

One related trap: a Slackware package filename is `PRGNAM-VERSION-ARCH-BUILD`,
but VERSION may itself contain a dash (`solvespace-3.1-659215d`), so the
PRGNAM/VERSION boundary cannot be found by counting dashes. Lookups anchor on
the package name being searched for instead.

### nvtake

When a package's installed version matches upstream, `nvtable` runs
`nvtake` for it, so nvchecker's `old_ver.json` tracks the state of this system
rather than only what upstream last did. Pass `-n` to disable.

Because a login shell can start while another is still running, the take is
serialised with `flock`; `nvtake` rewrites `old_ver.json` without locking of its
own. If the lock is held the take is skipped, and the next run picks it up.

## Installation

```
ln -s "$PWD/nvtable" ~/bin/nvtable
cp nvchecker.toml ~/.config/nvchecker/nvchecker.toml
```

The config expects a GitHub token in `~/.config/nvchecker/keys.toml`, without
which the many GitHub sources exhaust the anonymous rate limit:

```toml
[keys]
github = "ghp_..."
```

To show the table at login, call `nvtable` where `nvcmp` would have gone. The
repository paths `nvtable` scans are the `REPOS` array at the top of the script.

## Notes

`nvtable` reads `new_ver.json` and does not refresh it. If that file is stale,
for example right after renaming stanzas, the table reports the old names as
untracked until `nvchecker` runs again.

## License

GPLv2 (v2 only). See [LICENSE](LICENSE).

## Development Approach

This project is developed using AI-assisted tools. Code is generated with the help of AI based on human-provided specifications, design decisions, and iterative feedback.

All contributions are reviewed, tested, and curated by the maintainer before being included in the codebase. AI is used as a productivity and exploration tool, while human oversight remains central to all decisions.

The goal is to combine the flexibility of AI-assisted development with standard open-source practices such as transparency, review, and accountability.
