feat(info): show the install reason and drop the indentation

N? and `info PKG` now print three lines, none indented:

    PKG: <description>
    reason: explicitly installed | installed as a dependency | unknown | not installed
    required by: <installed dependents, comma separated> | nothing

The reason comes from a new <be>_reason backend function: apt-mark
showauto / showmanual (dpkg decides for a package in neither list), the
Install Reason of pacman -Qi, dnf repoquery --installed %{reason} (dnf4
and dnf5 words) and the Installed line of zypper info (rpm plus libzypp's
AutoInstalled when zypper is locked). A row whose old version is "-" is
not installed and the backend is not asked. The "(installed)" qualifier
moves to --help, README and DESIGN.

The rpm E2E fixture and e2e_reset now also mark su-dummy-a and su-dummy-c
as user-installed on dnf, which records plain rpm installs as "unknown",
so 1? and 2? show both reasons on all 12 distros. The verified commands
are recorded in discovery/{apt,pacman,rpm}.md.
This commit is contained in:
bryanpedini committed 2026-10-10 13:32:42 +02:00
1 parent 1354db6c39
commit 71d7f87aa3
21 files changed
+589 -48

No files matched your search

+11 -1
View File
@@ -98,7 +98,7 @@ Command grammar (one line, whitespace-separated tokens, each `N` is the number i
| token | action |
|--------|---------------------------------------------------------------------------|
| empty line | proceed: run the native upgrade with the current exclusions |
| `N?` | print the one-line description and the list of installed packages that depend on it |
| `N?` | print three lines, none indented: `PKG: <one-line description>`, `reason: <r>` with `<r>` = `explicitly installed`, `installed as a dependency`, `unknown` (the backend cannot tell) or `not installed` (also, without asking the backend, for a row whose old version is `-`), and `required by: <a, b, ...>` - the reverse dependencies, installed packages only - or `required by: nothing` |
| `N??` | print the full native info dump (`apt-cache show` / `pacman -Qi` / `dnf info` / `zypper info --requires`) |
| `N-` | exclude from this run (toggle; `N-` again re-includes); shown with `-` marker |
| `Nx` | purge it now, following pacman's `-Rns` principle on every backend: if any installed package still depends on it, refuse and print who (the reverse dependencies `N?` shows) - never cascade; otherwise simulate the native purge of it together with its now-unneeded dependencies, show that removal set ("This also removes: ...") and ask `y/N`. As a safety check, a simulated set containing anything that is neither the target nor in its installed dependency closure is refused too. Only a purge that ran removes rows (of every package it removed). A row whose old version is `-` (a new dependency, not installed) cannot be purged |
@@ -202,6 +202,7 @@ Every backend is a set of bash functions with a common prefix. All output is
<be>_describe PKG # stdout: one-line summary
<be>_full_info PKG # stdout: native full dump (candidate version if available)
<be>_rdepends PKG # stdout: installed packages requiring PKG, one per line
<be>_reason PKG # stdout: explicit | dependency | unknown | not-installed (how PKG got installed)
<be>_closure PKG # stdout: installed recursive dependency closure of PKG (excluding PKG), one per line
<be>_purge_plan PKG # stdout: every package a purge of PKG and its now-unneeded dependencies removes
# (simulation, no cascade); non-zero = refused
@@ -213,6 +214,15 @@ Every backend is a set of bash functions with a common prefix. All output is
Mutating calls go through `run_cmd` which honours `--dry-run` (prints `+ cmd` to
stdout instead of executing).
`<be>_reason` sources (read-only, unprivileged, verified in `discovery/*.md`): apt
`apt-mark showauto PKG` (dependency) / `apt-mark showmanual PKG` (explicit), a package in neither
list is explicit when dpkg has it installed; pacman the `Install Reason` of `pacman -Qi`; dnf
`repoquery --installed --queryformat '%{reason}'` (`user`/`group`/dnf5 `External User` = explicit,
`dependency`/`weak-dependency` = dependency, dnf4 `unknown` - plain `rpm -i` - stays unknown, no
output = not installed); zypper the `Installed` line of `zypper --no-refresh info`
(`Yes (automatically)` / `Yes` / `No`), and when zypper gives no answer (its lock is busy) `rpm -q`
plus libzypp's `/var/lib/zypp/AutoInstalled`.
## Layout
```