Files
command-center/omp-docs/tools/packages.md
T
Frank 40b9725433 fix(pkg-inventory): state the orphan law, test the boundary it rests on, stop --help mid-code
Three things from the review (#60), one of them a finding of my own that did not
survive measurement:

- The reported defect — additions never subtract `pacman -Qtdq`, so an orphan can
  be emitted as a manifest line — is wrong. A package carries one install reason:
  `-Qtdq` selects installed-as-dependency, additions come from `-Qqe`, so the sets
  are disjoint (blankpad: 206 explicit, 3 orphans, none in common). A subtraction
  would be dead code that looks like a guard, so the header now says why not to
  add one. The packages page law was rewritten to the same effect.
- What the old test should have pinned: an implicit dependency installed AFTER
  the baseline. Only `-Qqe` keeps it out of drift — mutate that query to `-Qq` and
  the new test goes red (verified), where the old assertion passed no matter what.
- `--help` printed `set -euo pipefail` and the LC_ALL line as documentation: the
  hardcoded sed range ran past the comment block. It now stops at the first line
  of code, so the header can keep growing.

Refs #60
2026-10-21 00:54:14 -07:00

5.9 KiB
Raw Blame History

type, title, description, tags, verified, timestamp
type title description tags verified timestamp
reference package desired state packages/*.pkgs plus the two tools that keep a machine and its manifest agreeing — install.sh installs desired-minus-installed, pkg-inventory.sh reports installed-minus-declared.
packages
pacman
provisioning
fleet
blankpad
2026-10-21 2026-10-20

Package desired state

Two tools, one invariant: the manifests must describe the machine. A rebuild is only faithful if both directions are checked.

tool direction writes
scripts/install.sh desired → installed installs desired − installed with --apply; never removes
scripts/pkg-inventory.sh installed → declared nothing — read-only report

Manifests

packages/common.pkgs (every host) + packages/<host>.pkgs (this host) + packages/aur.pkgs (AUR-only, installed through yay). The parser is sed 's/#.*//' then the first field, so # comments trail freely and one token per line is the whole grammar. An unknown --host gets the common manifest only: a wrong-host run is a no-op, not a hazard.

Rebuild a workstation

  1. Install the OS (CachyOS, niri/noctalia profile). The installer's own package set is not in the manifests and must not be — see the baseline law below.
  2. Clone this repo to ~/.omp, then scripts/install.sh --apply — packages first, because the enablement step needs sshd and firewalld present (dotfiles-deploy).
  3. scripts/deploy.sh sync for the config tier (dotfiles-deploy).
  4. scripts/pkg-inventory.sh --emit on a known-good machine and fold the output into packages/<host>.pkgs / packages/aur.pkgs before step 2 — that is how a hand install stops being invisible to the next rebuild.

pkg-inventory report

undeclared additions (declare these): N        ← exit 1 when N > 0
  repo (sync dbs): <names>                     → belongs in packages/<host>.pkgs
  aur (needs a helper): <names>                → belongs in packages/aur.pkgs
installer-provided, undeclared: N + names      → a reinstall supplies these; do not declare
declared, not installed: <names>               → install.sh installs these
orphans ignored: <names>                       → pacman -Qtdq, never drift (see the law below)
flatpak: <app ids>                             → reported only, install.sh installs no flatpaks

Exit codes: 0 clean · 1 drift · 2 usage · 3 classification impossible. Overrides PKGINV_REPO / PKGINV_HOST / PKGINV_LOG / PKGINV_BASELINE exist for tests.

Law: the installer's own day is the baseline

Whether a package is yours is decided by the pacman log, not by opinion: the baseline date is the oldest [ALPM] installed entry — Calamares' pacstrap — and everything installed on or before it is installer furniture. Without that cut, a base install reports several hundred "additions" and the list nobody can act on is worse than no list.

  • Read the rotated logs too (pacman.log.N, .N.gz). On a machine older than the rotation window the install day exists only there; skip them and the whole base system is reported as hand-installed.
  • Calamares keeps installing the desktop for minutes after the base transaction, which is why the date cut is inclusive.

Law: refuse rather than guess the AUR split

aur.pkgs vs <host>.pkgs is decided by whether any sync database can serve the name — Chaotic-AUR counts as a sync database, so only true AUR belongs in aur.pkgs. If the sync databases are unavailable the tool exits 3 instead of assuming: guessing sends every package to yay, which is how a manifest becomes a pile of source builds.

Classify with pacman -Sl (world-readable). pacman -Sqq requires root — a tool that only reads the machine must not need sudo to answer a question about it.

Known gap: aur.pkgs is read for every host, so a desktop-only AUR package — the niri module nirimod-git, say, or a GUI client — cannot be declared there without asking the headless servers to build it. Such a name is still reported as an AUR addition and stays undeclared (see the note in packages/blankpad.pkgs) until install.sh grows a per-host AUR manifest.

Law: orphans never reach a manifest

Drift is pacman -Qqe — explicitly installed, therefore yours. pacman -Qtdq names packages installed as a dependency that nothing requires any more, and a package carries exactly one install reason, so the two sets cannot overlap (blankpad 2026-10-21: 206 explicit, 3 orphans, none in common). That is why nothing in pkg-inventory.sh subtracts the orphan list from the additions: the query is already the guard. Do not add such a subtraction — it cannot fire, and it would bury the invariant that matters, an implicit install is never drift no matter when it arrived, which tests/pkg-inventory.bats pins with a dependency installed after the baseline. What the orphans ignored line is for is the other half: dead weight on the machine that a manifest must never teach a fresh box to install.

Two traps that produced this tool

  • set -o pipefail plus head -1: head closes the pipe, sort dies of SIGPIPE, the substitution returns 141, and set -e aborts the script. It survives a small test fixture and fails on a real 1000-line install-day log — take a minimum in one pass (awk 'END'-style), never sort-then-first. The same applies to awk 'NR==1{exit}', which closes the pipe exactly as head does.
  • tr '\n' ' ' swallows the final newline and fold faithfully preserves that absence, so the next report label lands on the same line as the last name. tr '\n' ' '; echo instead.
  • tools/index · dotfiles-deploy (the config tier that follows) · live-sync (the heartbeat that commits machine state)
  • Tests: tests/pkg-inventory.bats (fixtures + stubbed pacman, no real package database touched).
  • Refs #51.