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:
1 parent
1354db6c39
commit
71d7f87aa3
21 files changed
+589
-48
No files matched your search
@@ -704,3 +704,42 @@ Inside `proof.sh` (warm containers): forced refresh (`su_refresh`) 0.5-2.6 s (up
|
||||
* zypper exit codes 100-103, 106, 107 were not reproduced (see 7.2). `opensuse-slowroll`, MicroOS/Aeon (transactional), SLES were not tested.
|
||||
* Multi-arch (x86_64 + i686) and installonly kernels were not exercised; the parser keys on `name.arch` and keeps the highest installed `evr` per key for the "old" column.
|
||||
* Docker images pulled during this work (all kept, none removed or re-tagged): `almalinux:9`, `almalinux:10`, `rockylinux:9`, `rockylinux/rockylinux:9`, `rockylinux/rockylinux:10`, `quay.io/rockylinux/rockylinux:9`, `quay.io/rockylinux/rockylinux:10`, `opensuse/tumbleweed:latest`, `opensuse/leap:latest`, `fedora:latest`.
|
||||
|
||||
## 14. Install reason (`N?` / `info`)
|
||||
|
||||
Verified 2026-10-10, fixture `SU_FIXTURE_ISOLATE=1 SU_FIXTURE_ORPHAN=1`, `LC_ALL=C`.
|
||||
|
||||
### 14.1 DNF: `dnf -q repoquery --installed --queryformat '%{reason}\n' PKG`
|
||||
|
||||
Same answers on almalinux:9 and rockylinux/rockylinux:9 (dnf 4.14.0), almalinux:10 and rockylinux/rockylinux:10 (dnf 4.20.0); fedora:latest (Fedora 44, dnf5 5.4.3.0) uses other words:
|
||||
|
||||
| state | dnf4 | dnf5 |
|
||||
|---|---|---|
|
||||
| installed with plain `rpm -Uvh` (fixture's a, b, c) | `unknown` | `External User` |
|
||||
| `su-dummy-orphan` (pulled in by `dnf install su-dummy-orphan-parent`) | `dependency` | `Dependency` |
|
||||
| `dnf install su-dummy-a` pulling in `su-dummy-b` | a `user`, b `dependency` | a `User`, b `Dependency` |
|
||||
| not installed (in a repo or unknown) | no output, rc 0 | no output, rc 0 |
|
||||
|
||||
* `%{reason}` exists on every target, `--qf` and `--queryformat` both work. dnf4 ends the value with a newline, dnf5 does not (several installed versions would run together), so the format carries its own `\n` and the first non-empty line is used. Other values in the code: `weak-dependency`/`Weak Dependency` (dependency), `group`/`Group` (explicit: asked for with its group); dnf5 `External User` counts as user-installed (`repoquery --userinstalled` lists those packages) -> explicit; dnf4 `unknown` -> unknown.
|
||||
* Fallback `dnf history userinstalled` not needed and not used: dnf5 has no such command (`Unknown argument "userinstalled"`, rc 2), and on dnf4 it lists the fixture's rpm-installed a, b, c (reason `unknown`) as "Packages installed by user", i.e. it would call b explicit.
|
||||
* Marks: dnf4 `dnf mark remove PKG` -> `dependency`, `dnf mark install PKG` -> `user` (`mark user`/`mark dependency`: invalid choice, rc 2); dnf5 `dnf -y mark dependency|user PKG` (without `-y`: "Operation aborted", rc 1; `mark remove|install`: unknown argument, rc 2). A mark survives a later `dnf upgrade` and an `rpm -Uvh --oldpackage --replacepkgs` back to 1.0 (the e2e reset).
|
||||
* Works as `nobody` (setpriv / python3 on almalinux:10), with or without `-C`, in 110-150 ms (dnf4) / 45 ms (dnf5), and does not touch the repositories even with the distro repos re-enabled and their metadata expired.
|
||||
* Fixture consequence: on dnf the rpm-installed a and c would print `reason: unknown`; `SU_FIXTURE_B_AUTO=1` now also runs `dnf mark install su-dummy-a su-dummy-c` (dnf5: `-y mark user`), and `e2e_reset` re-marks a/c user and b dependency.
|
||||
|
||||
### 14.2 ZYPPER: `Installed` line of `zypper --non-interactive --no-refresh --quiet info PKG`
|
||||
|
||||
opensuse/tumbleweed (zypper 1.14.101, libzypp 17.38.16) and opensuse/leap 16.0 (same versions), identical, fixture with `SU_FIXTURE_B_AUTO=1`:
|
||||
|
||||
```
|
||||
su-dummy-a Installed : Yes rc 0
|
||||
su-dummy-b Installed : Yes (automatically) rc 0 (listed in /var/lib/zypp/AutoInstalled)
|
||||
su-dummy-orphan Installed : Yes (automatically) rc 0 (pulled in by zypper install su-dummy-orphan-parent)
|
||||
su-dummy-orphan-parent Installed : No rc 0 (Status : not installed)
|
||||
bash Installed : Yes rc 0 (Repository : @System, repos isolated)
|
||||
no-such-pkg package 'no-such-pkg' not found. rc 104
|
||||
```
|
||||
|
||||
* The block also has `Installed Size : 15 B`: match `^Installed *:` only.
|
||||
* Cross-check: the flag is exactly membership in `/var/lib/zypp/AutoInstalled` (bare package names, plus `pattern:...` lines and `#` comments; 0644 root, world-readable). Deleting `su-dummy-b` from it turns the line into `Yes` immediately; an `rpm -Uvh --replacepkgs` reinstall keeps it; `zypper install su-dummy-a` (rpm -e'd first) installs b with `Yes (automatically)` and adds it to the file; naming b on the command line (`zypper update su-dummy-a su-dummy-b`) drops the flag (b becomes user-installed).
|
||||
* Works as `nobody`. As root, while another process owns the zypp lock (`/run/zypp.pid` with a live pid), `zypper info` exits 7 ("System management is locked by the application with pid N (sleep)") - as `nobody` it still answers. Fallback in that case (and for exit 104): `rpm -q PKG` fails -> not installed; else membership in AutoInstalled -> dependency / explicit; unreadable -> unknown.
|
||||
* Fixture: a/c (plain rpm, not in AutoInstalled) are `Yes`, b is `Yes (automatically)` with `SU_FIXTURE_B_AUTO=1`; `e2e_reset` re-adds b to AutoInstalled. No change needed.
|
||||
Reference in new issue
Block a user