feat(show): merge the installed record with the upstream candidate
N?? and `show PKG` now print the installed record in the package
manager's own key/value layout, after one line explaining the format:
# lines starting with > come from the upstream candidate: a differing
value, or a field the installed record lacks
After each field whose candidate value differs comes the candidate's
line prefixed with "> "; fields only the candidate has are appended the
same way; installed-only fields stay; identical fields print once.
Multi-line values (pacman Optional Deps, dnf Description, RFC-822
continuation lines) are compared as blocks and a differing candidate
block gets "> " on every line. A package that is not installed shows the
candidate alone ("# not installed: this is the upstream candidate").
Merged where the backend gives comparable records for both sides:
- pacman: -Qi vs -Si REPO/PKG, REPO = the first repository -Sl lists
- apt: dpkg-query -s (Status, Conffiles, the long description; richer
than apt-cache show PKG=<installed>, which gives the archive record
when that version is still listed) vs apt-cache show --no-all-versions;
Description-xx pairs with Description and a one-line candidate
Description equal to the installed first line counts as identical
(the archives ship the long text in Translation files)
- dnf: info --installed vs info --upgrades (--available lists other
architectures first and nothing for an up-to-date package); not
installed: --available; dnf5's differently padded keys are re-padded
zypper keeps `zypper info --requires`: rpm -qi and zypper info share
almost no keys and zypper cannot print the installed record itself.
No candidate, or not exactly one record per side, keeps the old output.
Formats verified in containers and recorded in discovery/*.md. Unit
tests replay recorded pacman, apt, dnf4 and dnf5 records; the E2E suite
checks 2?? on su-dummy-b on all 12 distros.
This commit is contained in:
1 parent
296c77e319
commit
875c8294bf
30 files changed
+857
-45
No files matched your search
@@ -42,7 +42,7 @@ commands:
|
||||
untrust PKG... remove rows for PKG (and rows whose reason is dep:PKG)
|
||||
refresh recompute dependency closures for every 'user' root
|
||||
info PKG description, install reason and installed dependents (as N?)
|
||||
show PKG full native package information (as N??)
|
||||
show PKG installed record merged with the upstream candidate (as N??)
|
||||
|
||||
options:
|
||||
--backend=NAME force apt|dnf|pacman|zypper (default: detect pacman > apt > dnf/yum > zypper)
|
||||
@@ -128,7 +128,7 @@ listed prints an error, runs nothing, and the prompt comes back.
|
||||
|-------|--------|
|
||||
| empty line | proceed: run the native upgrade with the current exclusions |
|
||||
| `N?` | what is it: the one-line description, `reason:` how it got installed (`explicitly installed`, `installed as a dependency`, `unknown` when the package manager cannot tell, `not installed` for a new dependency) and `required by:` the packages that depend on it, or `nothing`. That list covers installed packages only |
|
||||
| `N??` | full native info (`apt-cache show`, `pacman -Qi` + `-Si`, `dnf info`, `zypper info --requires`) |
|
||||
| `N??` | full native info: the installed record, with the upstream candidate merged in as lines starting with `>` (see below). zypper shows `zypper info --requires` as it is |
|
||||
| `N-` | exclude from this run (toggle; `N-` again re-includes); shown with a `-` marker |
|
||||
| `Nx` | purge now, with its now-unneeded dependencies: the purge is simulated first and the prompt lists everything it removes (`Purge su-dummy-a now? This also removes: su-dummy-b [y/N]`); the answer is read from the same stdin; the rows of every removed package leave the list. `x` refuses when other installed packages depend on the target, like `pacman -Rns`; purge the dependents first (`not purged: su-dummy-b is required by: su-dummy-a - purge the dependents first`). Rows with old version `-` (new dependencies, not installed) cannot be purged |
|
||||
| `N+` | trust forever: write it and its installed dependency closure to the statefile; the row leaves the list. Rows this hides are re-included if they were excluded (a trusted package is upgraded) |
|
||||
@@ -142,6 +142,47 @@ Several tokens may share a line (`1? 3- 4+`); they run left to right. With `--al
|
||||
trusted packages are listed too, marked with `=`. When purges leave nothing to upgrade the
|
||||
session ends by itself (exit 0).
|
||||
|
||||
`N??` (and `show PKG`) prints the installed record in the package manager's own layout
|
||||
(`dpkg-query -s`, `pacman -Qi`, `dnf info --installed`), preceded by one `#` line saying how to read
|
||||
it. Right after each field whose value differs in the upstream candidate (`apt-cache show`,
|
||||
`pacman -Si` from the repository pacman would use, `dnf info --upgrades`) comes the candidate's line,
|
||||
prefixed with `>`; fields only the candidate has (download size, checksums, repository) follow at the
|
||||
end the same way, and fields only the installed record has (install reason, conffiles,
|
||||
`Required By`) stay as they are. Identical fields are printed once; multi-line values (descriptions, pacman
|
||||
`Optional Deps`, apt continuation lines) are compared as blocks and, when they differ, every line of
|
||||
the candidate's block gets the `>`. A package that is not installed shows the candidate alone. When
|
||||
the two records cannot be paired (zypper, whose `rpm -qi` and `zypper info` share almost no keys; no
|
||||
candidate; several installed architectures) the plain native output is shown. On Debian 12, with
|
||||
`su-dummy-b` 1.0 installed and 2.0 available:
|
||||
|
||||
```
|
||||
> 2??
|
||||
# lines starting with > come from the upstream candidate: a differing value, or a field the installed record lacks
|
||||
Package: su-dummy-b
|
||||
Status: install ok installed
|
||||
Priority: optional
|
||||
Section: misc
|
||||
Maintainer: Skeptical Updater Fixture <su-fixture@example.invalid>
|
||||
Architecture: all
|
||||
Version: 1.0
|
||||
> Version: 2.0
|
||||
Conffiles:
|
||||
/etc/su-dummy/su-dummy-b.conf 607e2d3a119dfe832d937ce579ef1079
|
||||
Description: dummy package su-dummy-b for skeptical-updater tests
|
||||
This package only exists to exercise the package manager.
|
||||
.
|
||||
It ships one data file and one conffile (version 1.0).
|
||||
> Description: dummy package su-dummy-b for skeptical-updater tests
|
||||
> This package only exists to exercise the package manager.
|
||||
> .
|
||||
> It ships one data file and one conffile (version 2.0).
|
||||
> Description-md5: 27e6732d8f16131f7512e1d4f1855cc3
|
||||
> Filename: ./su-dummy-b_2.0_all.deb
|
||||
> Size: 824
|
||||
> MD5sum: 62274690c5324af5419515e9f430226b
|
||||
> SHA256: e1c87480693d98770356f1dc55616dc416f7e3c3f71dc3967e94dec77bb36c16
|
||||
```
|
||||
|
||||
Under the rows, `Note:` lines report pending work that is not a row: new dependencies or
|
||||
replacements only the pacman transaction (`-Sup`) shows, packages dnf installs to obsolete
|
||||
others, packages a Tumbleweed `dist-upgrade` removes. With no row but such work, the list
|
||||
@@ -277,7 +318,9 @@ signal during the exit handler, the holds lock kept while a restore failed, jour
|
||||
zypper configuration replayed only when no other run holds their lock, the pacman pre-check /
|
||||
database install / `-Su` sequence and stale throwaway databases, statefile locking and merge,
|
||||
the `mkdir` lock fallback (EPERM owner alive, no busy loop), an inaccessible state directory, the
|
||||
sudo re-exec of the file itself and its loop guard), and the prompt on a real pty (`script`):
|
||||
sudo re-exec of the file itself and its loop guard), the `N??` merge on recorded pacman, apt, dnf4
|
||||
and dnf5 records (changed, identical, installed-only, candidate-only and multi-line fields, apt's
|
||||
short candidate descriptions, dnf5's padding, the fallbacks), and the prompt on a real pty (`script`):
|
||||
cursor editing, Up/Down history recall, the stashed line, the bell at both ends, no history file.
|
||||
About 30 seconds.
|
||||
|
||||
@@ -296,7 +339,9 @@ mirror state. `su-dummy-b` is recorded as installed as a dependency on every bac
|
||||
`--asdeps`, `apt-mark auto`, `dnf mark remove`, zypper's `AutoInstalled`) and `su-dummy-a` as
|
||||
explicitly installed (dnf needs `dnf mark install`: it records plain `rpm` installs as
|
||||
`unknown`). Then `tests/e2e/*.bats` exercise the real package manager: banner, list, `N?` (the
|
||||
reason and `required by:` of both), `N??`, `N+` and the statefile, `trust`/`untrust`/`refresh`,
|
||||
reason and `required by:` of both), `N??` (su-dummy-b's installed 1.0 record with `> Version`, and
|
||||
`> Repository` on pacman and dnf; zypper's plain `zypper info`), `N+` and the statefile,
|
||||
`trust`/`untrust`/`refresh`,
|
||||
excluding `su-dummy-b` (with and without a pre-existing hold/lock/IgnorePkg/exclude on
|
||||
`su-dummy-c`, which must survive byte for byte), trusting re-including an excluded
|
||||
dependency, full upgrade, `Nx` (refused for su-dummy-b, which su-dummy-a needs; su-dummy-a
|
||||
|
||||
@@ -622,3 +622,15 @@ $ apt-mark showmanual su-dummy-a su-dummy-b su-dummy-c -> su-dummy-a (only t
|
||||
* A removed package with its configuration files kept (`rc`) is in neither list: not installed. A package installed with `dpkg -i` is manual (also `su-dummy-c` reinstalled with `dpkg -i` after `dpkg -r`).
|
||||
* apt-mark prints a native package without its architecture qualifier (`apt-mark showauto libc6:amd64` -> `libc6`) and nothing for an arch that is not installed (`libc6:i386` -> empty); a name that is not a package is treated as a pattern (`apt-mark showmanual 'su-dummy-?'` and `'su-dummy-.'` -> `su-dummy-a`), exact names win (`libapt-pkg6.0`, `libbz2-1.0`, `libdb5.3` answered for themselves). Hence the tool matches the output line by line against `PKG` (or `PKG` without `:arch`).
|
||||
* Fixture: `su-dummy-a`/`-c` are manual (`dpkg -i`), `su-dummy-b` is auto with `SU_FIXTURE_B_AUTO=1` (`apt-mark auto su-dummy-b`); no change needed.
|
||||
|
||||
## 14. Item 14 - full info (`N??` / `show`): `dpkg-query -s` merged with `apt-cache show --no-all-versions`
|
||||
|
||||
Verified 2026-10-10 on debian:12 (apt 2.6.1, dpkg 1.21.23), `LC_ALL=C`: the fixture (`SU_FIXTURE_OFFLINE=1 SU_FIXTURE_ORPHAN=1 SU_FIXTURE_B_AUTO=1`) and the real archive after `apt-get update` (`perl-base` 5.36.0-7+deb12u3 installed, +deb12u4 in bookworm-security; `bash`, up to date).
|
||||
|
||||
* Installed record, two candidates compared:
|
||||
* `dpkg-query -s PKG`: Package, Essential, Status, Priority, Section, Installed-Size, Maintainer, Architecture, Multi-Arch, Source, Version, Replaces, Provides, (Pre-)Depends, Recommends, Suggests, Breaks, Conflicts, Conffiles (multi-line), Description (short + long, always: dpkg keeps it), Homepage. rc 1 and `package 'x' is not installed and no information is available` for an unknown name; a removed package with config files left still answers (`Status: deinstall ok config-files`).
|
||||
* `apt-cache show PKG=<installed version>`: when that version is only in dpkg's status (the fixture's 1.0, installed with `dpkg -i`) it is the same record plus a computed `Description-md5`; when the version is still in the lists (bash) it is the **archive's** record instead: no Status, no Conffiles, only the short description (Debian ships the long text in Translation files), plus Tag, Filename, Size, MD5sum, SHA256.
|
||||
* Chosen: `dpkg-query -s` - the record of what is installed, with its local fields, independent of the lists. `Status` third word `not-installed`/`config-files` = not installed.
|
||||
* Candidate: `apt-cache show --no-all-versions PKG` (as before). Same RFC-822 layout (`Key: value`, continuation lines start with a space, ` .` = empty line), keys in another order. Against the installed record: Version, Installed-Size and changed relations differ; Status, Conffiles, Multi-Arch (sometimes) only installed; Description-md5, Tag, Filename, Size, MD5sum, SHA256 only in the candidate.
|
||||
* Description: on the real archive the candidate's `Description` is one line (perl-base: `Description: minimal Perl system`) while dpkg has the long text; with Translation files downloaded it is `Description-en:` with the long text (see item 3). So `Description-xx` pairs with `Description`, and a one-line candidate equal to the installed first line counts as the same description. The fixture's local repository carries the long text in `Packages`, so there the two blocks really differ (`(version 1.0)` vs `(version 2.0)`).
|
||||
* Several installed architectures of one name (M-A: same) give several `dpkg-query -s` records: not paired, the plain candidate is shown.
|
||||
@@ -585,3 +585,33 @@ no-such-pkg error: package 'no-such-pkg' was not found
|
||||
* `pacman -Qi sh` answers for the provider (`bash`), like the description and `Required By` already do.
|
||||
* Mapping: `Explicitly installed` -> explicit, `Installed as a dependency for another package` -> dependency, -Qi failing -> not installed, anything else (or no such line) -> unknown.
|
||||
* Fixture: `su-dummy-b` is installed `--asdeps`, `a`/`c` explicitly; `e2e_reset` re-applies both after its `pacman -U`. No change needed.
|
||||
|
||||
## 14. Full info (`N??` / `show`): `-Qi` merged with `-Si REPO/PKG`
|
||||
|
||||
Verified 2026-10-10 on archlinux:latest (pacman 7.1.0), `COLUMNS=0 LC_ALL=C`: the fixture (`FIXTURE_ISOLATE=1 FIXTURE_ORPHAN=1 FIXTURE_SYNC=1`) and a real upgradable package after `pacman -Sy` (`glib2` 2.88.3-1 installed, core 2.90.1-1).
|
||||
|
||||
```
|
||||
$ pacman -Qi su-dummy-b $ pacman -Si su-dummy/su-dummy-b
|
||||
Name : su-dummy-b Repository : su-dummy
|
||||
Version : 1.0-1 Name : su-dummy-b
|
||||
Description : Skeptical-updater dummy ... Version : 2.0-1
|
||||
Architecture : any Description : Skeptical-updater dummy ...
|
||||
URL : https://example.invalid/... Architecture : any
|
||||
Licenses : custom URL / Licenses / Groups / Provides / Depends On /
|
||||
Groups / Provides / Depends On / Optional Deps Optional Deps / Conflicts With / Replaces
|
||||
Required By : su-dummy-a Download Size : 388.00 B
|
||||
Optional For : None Installed Size : 4.00 B
|
||||
Conflicts With / Replaces Packager : su-fixture <...>
|
||||
Installed Size : 4.00 B Build Date : Tue Nov 14 22:13:20 2023
|
||||
Packager : su-fixture <...> Validated By : SHA-256 Sum
|
||||
Build Date : Tue Nov 14 22:13:20 2023
|
||||
Install Date : Sat Oct 10 11:55:39 2026
|
||||
Install Reason : Installed as a dependency for another package
|
||||
Install Script : No
|
||||
Validated By : None
|
||||
```
|
||||
|
||||
* Both are one record per target (blank line after it), every key padded to the same column (`:` at column 17 in both, C locale), so the keys pair one to one. `-Qi` only: `Required By`, `Optional For`, `Install Date`, `Install Reason`, `Install Script`. `-Si` only: `Repository`, `Download Size`. Both, usually different: `Version`, `Installed Size`, `Build Date`, `Validated By` (`None`/`Signature` installed, `SHA-256 Sum Signature` in the repo), `Depends On`/`Provides`/`Optional Deps` when they changed.
|
||||
* Multi-line: only `Optional Deps` (continuation lines indented to the value column, one entry each - glib2: `dconf: ...` / `glib2-devel: ...` / `gvfs: ...`); with `COLUMNS=0` nothing else wraps (`Provides` of glib2, 6 entries, stays one line).
|
||||
* The candidate is `-Si REPO/PKG` with REPO = the first repository `-Sl` lists PKG in (pacman's choice, the join `pacman_upgradable` uses); plain `-Si PKG` prints one record per repository that has PKG.
|
||||
* Missing sides: `-Qi` of a package only in a sync database fails (not installed: the candidate alone); a local-only package has no `-Si` record (the plain `-Qi` stays).
|
||||
@@ -743,3 +743,40 @@ no-such-pkg package 'no-such-pkg' not found. rc 104
|
||||
* 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.
|
||||
|
||||
## 15. Full info (`N??` / `show`): installed record merged with the candidate
|
||||
|
||||
Verified 2026-10-10, fixture `SU_FIXTURE_ISOLATE=1 SU_FIXTURE_ORPHAN=1 SU_FIXTURE_B_AUTO=1`, `LC_ALL=C`, plus real packages on almalinux:9 (`glibc` 2.34-275.el9_8 installed, 283.el9_8 in baseos; `bash`, up to date; `zsh`, not installed).
|
||||
|
||||
### 15.1 DNF: `dnf -q info --installed PKG` vs `dnf -q info --upgrades PKG`
|
||||
|
||||
dnf4 (almalinux:9 4.14.0, almalinux:10 4.20.0 - identical apart from sizes): one heading line (`Installed Packages`, `Available Upgrades`, `Available Packages`), then `Key : value` records, `:` at column 14 on both sides, `Description` continued by ` : text` lines (an empty paragraph is ` : `), a blank line after each record; `Size : 15 ` keeps trailing blanks. Same keys on both sides (Name, Version, Release, Architecture, Size, Source, Repository, `From repo` (installed only, when dnf installed it), Summary, URL, License, Description): Version/Release, Size (installed size vs download size, same key), Source, Repository (`@System` vs the repo id) and changed Summary/Description differ.
|
||||
|
||||
dnf5 (fedora:latest, 5.4.3.0): headings `Installed packages` / `Available upgrades` / `Available packages`; installed keys Name, Epoch, Version, Release, Architecture, `Installed size`, Source, `From repository`, Summary, URL, License, Description, Vendor (`:` at column 17); the candidate has `Download size`, `Installed size`, `Repository` instead of `From repository` (`:` at column 16). `Installed size` is the same value with different padding; `Vendor` is `<NULL>` installed and empty in the repo for the fixture.
|
||||
|
||||
* `--upgrades`, not `--available`, for an installed package: on almalinux:9 `info --available glibc` prints the **i686** build first and then x86_64 (both not installed), and nothing for the up-to-date bash (`Error: No matching Packages to list`, rc 1); `--upgrades glibc` prints exactly the x86_64 candidate. Not installed (`zsh`): `--installed` fails, `--available` prints the record.
|
||||
* Both rc 1 with no match (dnf4 `Error: No matching Packages to list`, dnf5 `No matching packages to list`, stderr).
|
||||
|
||||
### 15.2 ZYPPER: not merged
|
||||
|
||||
```
|
||||
$ rpm -qi su-dummy-b $ zypper -n --no-refresh info su-dummy-b
|
||||
Name : su-dummy-b Information for package su-dummy-b:
|
||||
Version : 1.0 -----------------------------------
|
||||
Release : 1 Repository : su-dummy
|
||||
Architecture: noarch Name : su-dummy-b
|
||||
Install Date: Sat Oct 10 12:02:01 2026 Version : 2.0-1
|
||||
Group : Unspecified Arch : noarch
|
||||
Size : 15 Vendor :
|
||||
License : MIT Installed Size : 15 B
|
||||
Signature : (none) Installed : Yes (automatically)
|
||||
Source RPM : su-dummy-b-1.0-1.src.rpm Status : out-of-date (version 1.0-1 installed)
|
||||
Build Date : Sat Oct 10 12:02:01 2026 Source package : su-dummy-b-2.0-1.src
|
||||
Build Host : 17756d8ad56d Summary : skeptical-updater dummy su-dummy-b (v2.0)
|
||||
Summary : skeptical-updater ... Description :
|
||||
Description : Dummy package su-dummy-b version 2.0 ...
|
||||
Dummy package su-dummy-b version 1.0 ...
|
||||
Distribution: (none)
|
||||
```
|
||||
|
||||
* Only Name and Summary pair directly. Version (version vs version-release), Arch/Architecture, Size (bytes) vs `Installed Size` (`15 B`), `Source RPM` vs `Source package` (different file names), the description layout (rpm: unindented lines after `Description :`; zypper: indented 4) would all need translating, and zypper cannot print the installed record in its own layout (`zypper info -r @System PKG`: `Repository '@System' not found by its alias, number, or URI.`, rc 3). `N??` stays `zypper info --requires` (opensuse/tumbleweed, zypper 1.14.101).
|
||||
+30
-1
@@ -99,7 +99,7 @@ Command grammar (one line, whitespace-separated tokens, each `N` is the number i
|
||||
|--------|---------------------------------------------------------------------------|
|
||||
| empty line | proceed: run the native upgrade with the current exclusions |
|
||||
| `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??` | print the full native info: the installed record merged with the upstream candidate (see "Full info" below); zypper: `zypper info --requires` as it is |
|
||||
| `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 |
|
||||
| `N+` | trust it: write it and its installed dependency closure to the statefile; the row leaves the list (with `--all` it stays, marked `=`). Rows that this trust hides are re-included if they were excluded |
|
||||
@@ -126,6 +126,33 @@ history by itself). readline draws the prompt and the line on stderr, as bash do
|
||||
stdin keeps the plain `IFS= read -r` with the prompt on stdout and each line echoed after it, so
|
||||
piped runs and their logs are unchanged.
|
||||
|
||||
Full info (`N??`, `show PKG`). The first line is `# lines starting with > come from the upstream
|
||||
candidate: a differing value, or a field the installed record lacks`; then the installed record in
|
||||
its native key/value layout, as the base. After each field whose candidate value differs, the
|
||||
candidate's line follows, prefixed with `> ` (same key formatting); fields only the candidate has
|
||||
are appended at the end the same way; fields only the installed record has stay; identical fields
|
||||
print once. A field starts on a line that begins with a non-blank character and holds a `:` (the
|
||||
key is the text before it); lines beginning with a blank continue it, so multi-line values (pacman
|
||||
`Optional Deps`, dnf `Description`, RFC-822 continuation lines) are compared as blocks, every line
|
||||
trimmed, and a differing candidate block gets `> ` on every line. `Description-xx` (apt Translation
|
||||
files) pairs with `Description`; on apt a one-line candidate `Description` equal to the first
|
||||
installed line counts as identical (the archives carry only the short description, the long text
|
||||
is in the Translation files). When both records align their `:` in one column but in different
|
||||
ones (dnf5: 16 installed, 15 available), the candidate lines are re-padded to the installed one's.
|
||||
|
||||
| backend | installed record | upstream candidate |
|
||||
|---------|------------------|--------------------|
|
||||
| pacman | `pacman -Qi PKG` | `pacman -Si REPO/PKG`, REPO = the first repository `pacman -Sl` lists PKG in (as for the review list) |
|
||||
| apt | `dpkg-query -s PKG` (Status, Conffiles, always the long description; `apt-cache show PKG=<installed>` gives the archive's record when that version is still listed: no Status/Conffiles, short description only); a `Status` of `not-installed`/`config-files` = not installed | `apt-cache show --no-all-versions PKG` |
|
||||
| dnf | `dnf -q info --installed PKG` | `dnf -q info --upgrades PKG` (installed architecture only; `--available` also lists other architectures and, for an up-to-date package, other builds); not installed: `--available` |
|
||||
| zypper | - | - (`rpm -qi` and `zypper info` share almost no keys - Version vs version-release, Arch, byte sizes, source names, description layout - and zypper cannot print the installed record by itself: `zypper info -r @System` is refused) |
|
||||
|
||||
A package that is not installed (a row whose old version is `-` is not asked) shows the candidate
|
||||
alone under `# not installed: this is the upstream candidate`. Anything else - no candidate (an
|
||||
up-to-date dnf package, a local-only pacman package), not exactly one record on each side (two
|
||||
installed architectures), zypper - prints `<be>_full_info` unchanged. `dnf info` headings
|
||||
("Installed Packages", ...) are dropped. Verified formats: `discovery/{pacman,apt,rpm}.md`.
|
||||
|
||||
Flags shown after a row: `[held]` = the package manager will not upgrade it on its own: on apt
|
||||
a hold (`apt-mark hold`) or a package apt keeps back (the "kept back" block of
|
||||
`apt-get -s --with-new-pkgs upgrade`), on pacman an `IgnorePkg` (`[ignored]` in `pacman -Qu`).
|
||||
@@ -213,6 +240,8 @@ Every backend is a set of bash functions with a common prefix. All output is
|
||||
# plus "#<note>" lines for pending work that is not a row
|
||||
<be>_describe PKG # stdout: one-line summary
|
||||
<be>_full_info PKG # stdout: native full dump (candidate version if available)
|
||||
<be>_installed_record PKG # optional (pacman, apt, dnf): stdout: ONE native record of the installed package
|
||||
<be>_candidate_record PKG installed|not-installed # optional, with the above: ONE native record of the candidate
|
||||
<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
|
||||
|
||||
+121
-6
@@ -91,7 +91,7 @@ commands:
|
||||
untrust PKG... remove rows for PKG (and rows whose reason is dep:PKG)
|
||||
refresh recompute dependency closures for every 'user' root
|
||||
info PKG description, install reason and installed dependents (as N?)
|
||||
show PKG full native package information (as N??)
|
||||
show PKG installed record merged with the upstream candidate (as N??)
|
||||
|
||||
options:
|
||||
--backend=NAME force apt|dnf|pacman|zypper (default: detect pacman > apt > dnf/yum > zypper)
|
||||
@@ -108,7 +108,8 @@ interactive commands (one line, whitespace-separated tokens, N = number in brack
|
||||
N? what is it: the description, 'reason:' (explicitly installed, installed as
|
||||
a dependency, unknown or not installed) and 'required by:', the packages
|
||||
that depend on it - installed packages only
|
||||
N?? full native info dump
|
||||
N?? full native info: the installed record; lines starting with '>' come
|
||||
from the upstream candidate (differing values, fields it adds)
|
||||
N- exclude from this run (toggle); shown with a '-' marker
|
||||
Nx purge it now with its now-unneeded dependencies (shown, asks y/N); refused
|
||||
when other installed packages depend on it, like pacman -Rns
|
||||
@@ -128,7 +129,7 @@ session_help() {
|
||||
cat <<'EOF'
|
||||
<enter> upgrade now, skipping the rows marked '-'
|
||||
N? what is it: description, install reason + installed packages that need it
|
||||
N?? full native info
|
||||
N?? full native info: installed record, '>' lines from the upstream candidate
|
||||
N- exclude N from this run (toggle)
|
||||
Nx purge N now: lists every package the purge removes, then asks y/N.
|
||||
x refuses when other installed packages depend on the target, like
|
||||
@@ -884,6 +885,18 @@ apt_describe() {
|
||||
printf '%s\n' "$out"
|
||||
}
|
||||
apt_full_info() { apt-cache show --no-all-versions "$1" </dev/null; }
|
||||
# apt_installed_record PKG: dpkg's own record (`dpkg-query -s`: richer than `apt-cache show
|
||||
# PKG=<installed version>`, which gives the archive's record when that version is still listed:
|
||||
# no Status, no Conffiles, often only the short description); a package dpkg merely remembers
|
||||
# (removed, config files left) is not installed. apt_candidate_record PKG: the candidate.
|
||||
apt_installed_record() {
|
||||
local out st
|
||||
out=$(dpkg-query -s -- "$1" </dev/null 2>/dev/null) || return 1
|
||||
st=$(sed -n 's/^Status: //p' <<<"$out")
|
||||
case ${st##* } in '' | not-installed | config-files) return 1 ;; esac
|
||||
printf '%s\n' "$out"
|
||||
}
|
||||
apt_candidate_record() { apt-cache show --no-all-versions "$1" </dev/null 2>/dev/null; }
|
||||
apt_rdepends() {
|
||||
apt-cache rdepends --installed --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances "$1" </dev/null 2>/dev/null |
|
||||
awk '/^Reverse Depends:/{p=1;next} p{sub(/^[ |]+/,""); print}' | sort -u
|
||||
@@ -1229,6 +1242,15 @@ pacman_full_info() {
|
||||
_pm -Si -- "$1" 2>/dev/null && ok=0
|
||||
return "$ok"
|
||||
}
|
||||
# pacman_installed_record PKG: -Qi. pacman_candidate_record PKG: -Si of the repository pacman would
|
||||
# take it from (the first one `-Sl` lists it in, as in pacman_upgradable)
|
||||
pacman_installed_record() { _pm -Qi -- "$1" 2>/dev/null; }
|
||||
pacman_candidate_record() {
|
||||
local repo
|
||||
repo=$(_pm -Sl 2>/dev/null | awk -v p="$1" '$2 == p && r == "" { r = $1 } END { if (r != "") print r }') || repo=''
|
||||
[[ -n $repo ]] || return 1
|
||||
_pm -Si -- "$repo/$1" 2>/dev/null
|
||||
}
|
||||
pacman_rdepends() {
|
||||
local out w
|
||||
local -a words=()
|
||||
@@ -1619,6 +1641,16 @@ dnf_describe() {
|
||||
return 1
|
||||
}
|
||||
dnf_full_info() { "$DNF" -q info "$1" </dev/null; }
|
||||
# dnf_installed_record PKG / dnf_candidate_record PKG installed|not-installed: `dnf info` blocks
|
||||
# without their heading line ("Installed Packages", dnf5 "Available upgrades", ...). The candidate
|
||||
# of an installed package is its upgrade (--upgrades: the installed architecture only, nothing for
|
||||
# an up-to-date package; --available would also list other architectures and older builds).
|
||||
dnf_installed_record() { "$DNF" -q info --installed "$1" </dev/null 2>/dev/null | sed '/^[^[:space:]][^:]*$/d'; }
|
||||
dnf_candidate_record() {
|
||||
local w=--available
|
||||
[[ ${2:-} != installed ]] || w=--upgrades
|
||||
"$DNF" -q info "$w" "$1" </dev/null 2>/dev/null | sed '/^[^[:space:]][^:]*$/d'
|
||||
}
|
||||
dnf_rdepends() { rpm_rdeps "$1"; }
|
||||
dnf_closure() { rpm_closure "$1"; }
|
||||
# dnf_reason PKG: dnf's own record, `repoquery --installed --queryformat %{reason}` (discovery/rpm.md
|
||||
@@ -2078,8 +2110,91 @@ print_info() {
|
||||
return "$rc"
|
||||
}
|
||||
|
||||
print_full_info() { # print_full_info PKG (N?? / show); 1 if the package manager knows nothing
|
||||
local rc=0
|
||||
# N?? / show: the installed record as the base, merged with the upstream candidate. Backends that
|
||||
# give comparable key/value records for both sides define <be>_installed_record PKG and
|
||||
# <be>_candidate_record PKG installed|not-installed (one native record each, no output = none):
|
||||
# pacman, apt, dnf. zypper does not (rpm -qi and zypper info share almost no keys, and zypper cannot
|
||||
# print the installed record by itself) and keeps <be>_full_info, as does any package for which
|
||||
# either side is missing or is not exactly one record.
|
||||
readonly SU_MERGE_NOTE='# lines starting with > come from the upstream candidate: a differing value, or a field the installed record lacks'
|
||||
readonly SU_CANDIDATE_NOTE='# not installed: this is the upstream candidate'
|
||||
# merge: each base field is printed as it is; when the candidate's value differs, the candidate's
|
||||
# line(s) follow, prefixed with "> "; fields only the candidate has are appended the same way. A
|
||||
# field starts on a line beginning with a non-blank character that holds a ":" (key = the text
|
||||
# before it, trailing blanks dropped; a heading without ":" before the record is skipped); lines
|
||||
# beginning with a blank continue it (pacman Optional Deps, dnf Description, RFC-822 continuation
|
||||
# lines), so multi-line values are compared - and prefixed - as blocks, every line trimmed. A blank
|
||||
# line ends a record: exit 3 unless each side holds exactly one. Description-xx (apt Translation
|
||||
# files) pairs with Description. apt=1: the archives carry only the short description of the
|
||||
# candidate (the long text is in the Translation files), so a one-line candidate Description equal
|
||||
# to the first installed line is the same description. When both records align every ":" in one
|
||||
# column (pacman, dnf) but in different ones (dnf5 pads installed and available keys differently),
|
||||
# the candidate lines are re-padded to the installed record's column.
|
||||
# shellcheck disable=SC2016 # awk program, not shell
|
||||
readonly AWK_MERGE_RECORDS='
|
||||
function trim(s) { sub(/^[ \t]+/, "", s); sub(/[ \t\r]+$/, "", s); return s }
|
||||
function repad(l, i, k) {
|
||||
if (shift == 0) return l
|
||||
if (l ~ /^[^ \t]/) { i = index(l, ":"); k = substr(l, 1, i - 1); sub(/[ \t]+$/, "", k); return sprintf("%-" (col[1] - 1) "s", k) substr(l, i) }
|
||||
if (shift > 0) return sprintf("%" shift "s", "") l
|
||||
return substr(l, 1 - shift)
|
||||
}
|
||||
function pfx(s, n, a, m) { n = split(s, a, "\n"); for (m = 1; m <= n; m++) print "> " repad(a[m]) }
|
||||
FNR == 1 { side = (FILENAME == ARGV[1]) ? 1 : 2; cur = "" }
|
||||
/^[ \t]*$/ { if (nk[side]) ended[side] = 1; next }
|
||||
/^[^ \t]/ {
|
||||
i = index($0, ":")
|
||||
if (i == 0) { if (nk[side]) bad = 1; next }
|
||||
if (ended[side]) { bad = 1; next }
|
||||
if (!nk[side]) col[side] = i
|
||||
else if (col[side] != i) col[side] = -1
|
||||
k = substr($0, 1, i - 1); sub(/[ \t]+$/, "", k)
|
||||
if (k ~ /^Description-[a-z][a-z](_[A-Z][A-Z])?$/) k = "Description"
|
||||
if (++seen[side, k] > 1) k = k "#" seen[side, k]
|
||||
key[side, ++nk[side]] = k
|
||||
raw[side, k] = $0; val[side, k] = trim(substr($0, i + 1)); first[side, k] = val[side, k]; nl[side, k] = 1
|
||||
cur = k
|
||||
next
|
||||
}
|
||||
{
|
||||
if (cur == "" || ended[side]) { bad = 1; next }
|
||||
raw[side, cur] = raw[side, cur] "\n" $0; val[side, cur] = val[side, cur] "\n" trim($0); nl[side, cur]++
|
||||
}
|
||||
END {
|
||||
if (bad || !nk[1] || !nk[2]) exit 3
|
||||
shift = (col[1] > 0 && col[2] > 0) ? col[1] - col[2] : 0
|
||||
for (j = 1; j <= nk[1]; j++) {
|
||||
k = key[1, j]
|
||||
print raw[1, k]
|
||||
if (!((2, k) in raw) || val[1, k] == val[2, k]) continue
|
||||
if (apt && k == "Description" && nl[2, k] == 1 && val[2, k] == first[1, k]) continue
|
||||
pfx(raw[2, k])
|
||||
}
|
||||
for (j = 1; j <= nk[2]; j++) { k = key[2, j]; if (!((1, k) in raw)) pfx(raw[2, k]) }
|
||||
}'
|
||||
|
||||
print_full_info() { # print_full_info PKG [OLD] (N?? / show); 1 if the package manager knows nothing
|
||||
local rc=0 inst='' cand='' out apt=0
|
||||
if declare -F "${BACKEND}_installed_record" >/dev/null; then
|
||||
[[ $BACKEND != apt ]] || apt=1
|
||||
# a row whose old version is "-" is a new dependency: not installed, nothing to ask
|
||||
[[ ${2:-} == - ]] || inst=$(be installed_record "$1") || inst=''
|
||||
if [[ -n $inst ]]; then
|
||||
cand=$(be candidate_record "$1" installed) || cand=''
|
||||
if [[ -n $cand ]] && out=$(awk -v apt="$apt" "$AWK_MERGE_RECORDS" <(printf '%s\n' "$inst") <(printf '%s\n' "$cand")); then
|
||||
say "$SU_MERGE_NOTE"
|
||||
say "$out"
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
cand=$(be candidate_record "$1" not-installed) || cand=''
|
||||
if [[ -n $cand ]]; then
|
||||
say "$SU_CANDIDATE_NOTE"
|
||||
say "$cand"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
be full_info "$1" || rc=$?
|
||||
((rc == 0)) && return 0
|
||||
err "no package information for '$1' ($(be label) exited $rc)"
|
||||
@@ -2369,7 +2484,7 @@ session_line() {
|
||||
i=$REPLY
|
||||
case $op in
|
||||
'?') print_info "${R_NAME[i]}" "${R_OLD[i]}" || true ;;
|
||||
'??') print_full_info "${R_NAME[i]}" || true ;;
|
||||
'??') print_full_info "${R_NAME[i]}" "${R_OLD[i]}" || true ;;
|
||||
-)
|
||||
if ((R_EXCL[i])); then
|
||||
R_EXCL[i]=0
|
||||
|
||||
@@ -56,10 +56,33 @@ zypper_verb() { case $(. /etc/os-release && printf '%s' "$ID") in opensuse-tumbl
|
||||
assert_line 'Quit: nothing was upgraded.'
|
||||
}
|
||||
|
||||
@test "N?? prints the native full info" {
|
||||
run skeptical-updater --no-refresh <<<$'1??\nq'
|
||||
@test "N?? shows the installed record merged with the upstream candidate (zypper: zypper info)" {
|
||||
run skeptical-updater --no-refresh <<<$'2??\nq'
|
||||
assert_success
|
||||
assert_line --regexp '^(Package: su-dummy-a|Name +: su-dummy-a)$'
|
||||
if [[ $BE == zypper ]]; then # no comparable records: the plain zypper info --requires
|
||||
assert_line 'Information for package su-dummy-b:'
|
||||
refute_line --partial '# lines starting with >'
|
||||
return 0
|
||||
fi
|
||||
assert_line '# lines starting with > come from the upstream candidate: a differing value, or a field the installed record lacks'
|
||||
case $BE in
|
||||
apt)
|
||||
assert_line 'Package: su-dummy-b'
|
||||
assert_line 'Status: install ok installed'
|
||||
assert_output --partial "$(printf '%s\n' 'Version: 1.0' '> Version: 2.0')"
|
||||
assert_line --regexp '^> Filename: .*su-dummy-b_2\.0_all\.deb$'
|
||||
;;
|
||||
pacman)
|
||||
assert_output --partial "$(printf '%s\n' 'Version : 1.0-1' '> Version : 2.0-1')"
|
||||
assert_line 'Install Reason : Installed as a dependency for another package'
|
||||
assert_line '> Repository : su-dummy'
|
||||
;;
|
||||
dnf)
|
||||
assert_output --partial "$(printf '%s\n' 'Version : 1.0' '> Version : 2.0')"
|
||||
assert_output --partial "$(printf '%s\n' 'Repository : @System' '> Repository : su-dummy')"
|
||||
;;
|
||||
esac
|
||||
[[ $(grep -c '^> ' <<<"$output") -ge 2 ]]
|
||||
}
|
||||
|
||||
@test "N+ writes the user row and the dep row; list hides both and counts them" {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
Package: perl-base
|
||||
Source: perl
|
||||
Version: 5.36.0-7+deb12u4
|
||||
Installed-Size: 7643
|
||||
Maintainer: Niko Tyni <ntyni@debian.org>
|
||||
Architecture: amd64
|
||||
Replaces: libfile-path-perl (<< 2.18), libfile-temp-perl (<< 0.2311), libio-socket-ip-perl (<< 0.41), libscalar-list-utils-perl (<< 1:1.62), libsocket-perl (<< 2.033), libxsloader-perl (<< 0.31), perl (<< 5.10.1-12), perl-modules (<< 5.20.1-3)
|
||||
Provides: libfile-path-perl (= 2.18), libfile-temp-perl (= 0.2311), libio-socket-ip-perl (= 0.41), libscalar-list-utils-perl (= 1:1.62), libsocket-perl (= 2.033), libxsloader-perl (= 0.31), perlapi-5.36.0
|
||||
Suggests: perl, sensible-utils
|
||||
Conflicts: defoma (<< 0.11.12), doc-base (<< 0.10.3), mono-gac (<< 2.10.8.1-3), safe-rm (<< 0.8), update-inetd (<< 4.41)
|
||||
Breaks: amanda-common (<< 1:3.3.9-2), backuppc (<< 4.4.0-7~), bucardo (<< 5.5.0-1.1), debconf (<< 1.5.61), dh-haskell (<< 0.3), duck (<< 0.14.1), intltool (<< 0.51.0-4), kio-perldoc (<< 20.04.1-1), latexml (<< 0.8.4-2), libdevel-mat-dumper-perl (<< 0.42-3), libencode-arabic-perl (<< 14.2-2), libexception-class-perl (<< 1.42), libfile-path-perl (<< 2.18), libfile-spec-perl (<< 3.8400), libfile-temp-perl (<< 0.2311), libio-socket-ip-perl (<< 0.41), libmp3-tag-perl (<< 1.13-1.2), libsbuild-perl (<< 0.67.0-1), libscalar-list-utils-perl (<< 1:1.62), libsocket-perl (<< 2.033), libxsloader-perl (<< 0.31), mailagent (<< 1:3.1-81-2), perl (<< 5.36.0~), perl-modules (<< 5.36.0~), pod2pdf (<< 0.42-5.1), slic3r (<< 1.2.9+dfsg-6.1), slic3r-prusa (<< 1.37.0+dfsg-1.1), texinfo (<< 6.1.0.dfsg.1-8)
|
||||
Description: minimal Perl system
|
||||
Description-md5: 6f98ca50727514fe6b19872d5ede03ec
|
||||
Homepage: http://dev.perl.org/perl5/
|
||||
Essential: yes
|
||||
Pre-Depends: libc6 (>= 2.35), libcrypt1 (>= 1:4.1.0), dpkg (>= 1.17.17)
|
||||
Section: perl
|
||||
Priority: required
|
||||
Filename: pool/updates/main/p/perl/perl-base_5.36.0-7+deb12u4_amd64.deb
|
||||
Size: 1609596
|
||||
SHA256: d7d1943aec9597629bf73075efcc5ef6dc9bda96d78e80d843156ecd448478b8
|
||||
@@ -0,0 +1,21 @@
|
||||
Package: zsh
|
||||
Source: zsh (5.9-4)
|
||||
Version: 5.9-4+b15
|
||||
Installed-Size: 2461
|
||||
Maintainer: Debian Zsh Maintainers <pkg-zsh-devel@lists.alioth.debian.org>
|
||||
Architecture: amd64
|
||||
Depends: zsh-common (= 5.9-4), libc6 (>= 2.34), libcap2 (>= 1:2.10), libtinfo6 (>= 6), debianutils (>= 5.3-1~)
|
||||
Recommends: libc6 (>= 2.35), libgdbm6 (>= 1.16), libncursesw6 (>= 6), libpcre3
|
||||
Suggests: zsh-doc
|
||||
Description: shell with lots of features
|
||||
Description-md5: a129d6b2d23d2d5d3a6b822d3f8f856d
|
||||
Homepage: https://www.zsh.org/
|
||||
Tag: devel::interpreter, implemented-in::c, interface::shell,
|
||||
network::client, protocol::ftp, role::program, scope::utility
|
||||
Section: shells
|
||||
Priority: optional
|
||||
Filename: pool/main/z/zsh/zsh_5.9-4+b15_amd64.deb
|
||||
Size: 907512
|
||||
MD5sum: 831903f59bf1ed613a5beeef9fc787ad
|
||||
SHA256: 8362cc8bbb530d393950522dc8a9df5a860a61227d62ff669a7524056e6289a8
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
Package: perl-base
|
||||
Essential: yes
|
||||
Status: install ok installed
|
||||
Priority: required
|
||||
Section: perl
|
||||
Installed-Size: 7639
|
||||
Maintainer: Niko Tyni <ntyni@debian.org>
|
||||
Architecture: amd64
|
||||
Source: perl
|
||||
Version: 5.36.0-7+deb12u3
|
||||
Replaces: libfile-path-perl (<< 2.18), libfile-temp-perl (<< 0.2311), libio-socket-ip-perl (<< 0.41), libscalar-list-utils-perl (<< 1:1.62), libsocket-perl (<< 2.033), libxsloader-perl (<< 0.31), perl (<< 5.10.1-12), perl-modules (<< 5.20.1-3)
|
||||
Provides: libfile-path-perl (= 2.18), libfile-temp-perl (= 0.2311), libio-socket-ip-perl (= 0.41), libscalar-list-utils-perl (= 1:1.62), libsocket-perl (= 2.033), libxsloader-perl (= 0.31), perlapi-5.36.0
|
||||
Pre-Depends: libc6 (>= 2.35), libcrypt1 (>= 1:4.1.0), dpkg (>= 1.17.17)
|
||||
Suggests: perl, sensible-utils
|
||||
Breaks: amanda-common (<< 1:3.3.9-2), backuppc (<< 4.4.0-7~), bucardo (<< 5.5.0-1.1), debconf (<< 1.5.61), dh-haskell (<< 0.3), duck (<< 0.14.1), intltool (<< 0.51.0-4), kio-perldoc (<< 20.04.1-1), latexml (<< 0.8.4-2), libdevel-mat-dumper-perl (<< 0.42-3), libencode-arabic-perl (<< 14.2-2), libexception-class-perl (<< 1.42), libfile-path-perl (<< 2.18), libfile-spec-perl (<< 3.8400), libfile-temp-perl (<< 0.2311), libio-socket-ip-perl (<< 0.41), libmp3-tag-perl (<< 1.13-1.2), libsbuild-perl (<< 0.67.0-1), libscalar-list-utils-perl (<< 1:1.62), libsocket-perl (<< 2.033), libxsloader-perl (<< 0.31), mailagent (<< 1:3.1-81-2), perl (<< 5.36.0~), perl-modules (<< 5.36.0~), pod2pdf (<< 0.42-5.1), slic3r (<< 1.2.9+dfsg-6.1), slic3r-prusa (<< 1.37.0+dfsg-1.1), texinfo (<< 6.1.0.dfsg.1-8)
|
||||
Conflicts: defoma (<< 0.11.12), doc-base (<< 0.10.3), mono-gac (<< 2.10.8.1-3), safe-rm (<< 0.8), update-inetd (<< 4.41)
|
||||
Description: minimal Perl system
|
||||
Perl is a scripting language used in many system scripts and utilities.
|
||||
.
|
||||
This package provides a Perl interpreter and the small subset of the
|
||||
standard run-time library required to perform basic tasks. For a full
|
||||
Perl installation, install "perl" (and its dependencies, "perl-modules-5.36"
|
||||
and "perl-doc").
|
||||
Homepage: http://dev.perl.org/perl5/
|
||||
@@ -0,0 +1,14 @@
|
||||
Package: su-dummy-a
|
||||
Status: install ok installed
|
||||
Priority: optional
|
||||
Section: misc
|
||||
Maintainer: Skeptical Updater Fixture <su-fixture@example.invalid>
|
||||
Architecture: all
|
||||
Version: 1.0
|
||||
Depends: su-dummy-b
|
||||
Conffiles:
|
||||
/etc/su-dummy/su-dummy-a.conf 607e2d3a119dfe832d937ce579ef1079
|
||||
Description: dummy package su-dummy-a for skeptical-updater tests
|
||||
This package only exists to exercise the package manager.
|
||||
.
|
||||
It ships one data file and one conffile (version 1.0).
|
||||
@@ -0,0 +1,13 @@
|
||||
Package: su-dummy-b
|
||||
Status: install ok installed
|
||||
Priority: optional
|
||||
Section: misc
|
||||
Maintainer: Skeptical Updater Fixture <su-fixture@example.invalid>
|
||||
Architecture: all
|
||||
Version: 1.0
|
||||
Conffiles:
|
||||
/etc/su-dummy/su-dummy-b.conf 607e2d3a119dfe832d937ce579ef1079
|
||||
Description: dummy package su-dummy-b for skeptical-updater tests
|
||||
This package only exists to exercise the package manager.
|
||||
.
|
||||
It ships one data file and one conffile (version 1.0).
|
||||
@@ -0,0 +1,13 @@
|
||||
Package: su-dummy-c
|
||||
Status: install ok installed
|
||||
Priority: optional
|
||||
Section: misc
|
||||
Maintainer: Skeptical Updater Fixture <su-fixture@example.invalid>
|
||||
Architecture: all
|
||||
Version: 1.0
|
||||
Conffiles:
|
||||
/etc/su-dummy/su-dummy-c.conf 607e2d3a119dfe832d937ce579ef1079
|
||||
Description: dummy package su-dummy-c for skeptical-updater tests
|
||||
This package only exists to exercise the package manager.
|
||||
.
|
||||
It ships one data file and one conffile (version 1.0).
|
||||
@@ -0,0 +1,17 @@
|
||||
Available Packages
|
||||
Name : zsh
|
||||
Version : 5.8
|
||||
Release : 9.el9
|
||||
Architecture : x86_64
|
||||
Size : 2.9 M
|
||||
Source : zsh-5.8-9.el9.src.rpm
|
||||
Repository : baseos
|
||||
Summary : Powerful interactive shell
|
||||
URL : http://zsh.sourceforge.net/
|
||||
License : MIT
|
||||
Description : The zsh shell is a command interpreter usable as an interactive login
|
||||
: shell and as a shell script command processor. Zsh resembles the ksh
|
||||
: shell (the Korn shell), but includes many enhancements. Zsh supports
|
||||
: command line editing, built-in spelling correction, programmable
|
||||
: command completion, shell functions (with autoloading), a history
|
||||
: mechanism, and more.
|
||||
@@ -0,0 +1,19 @@
|
||||
Installed Packages
|
||||
Name : glibc
|
||||
Version : 2.34
|
||||
Release : 275.el9_8
|
||||
Architecture : x86_64
|
||||
Size : 6.2 M
|
||||
Source : glibc-2.34-275.el9_8.src.rpm
|
||||
Repository : @System
|
||||
From repo : baseos
|
||||
Summary : The GNU libc libraries
|
||||
URL : http://www.gnu.org/software/glibc/
|
||||
License : LGPL-2.1-or-later AND SunPro AND LGPL-2.1-or-later WITH GCC-exception-2.0 AND BSD-3-Clause AND GPL-2.0-or-later AND LGPL-2.1-or-later WITH GNU-compiler-exception AND GPL-2.0-only AND ISC AND LicenseRef-Fedora-Public-Domain AND HPND AND CMU-Mach AND LGPL-2.0-or-later AND Unicode-3.0 AND GFDL-1.1-or-later AND GPL-1.0-or-later AND FSFUL AND MIT AND Inner-Net-2.0 AND X11 AND GPL-2.0-or-later WITH GCC-exception-2.0 AND GFDL-1.3-only AND GFDL-1.1-only AND GPL-3.0-or-later AND GPL-3.0-or-later WITH Autoconf-exception-generic-3.0 AND GPL-3.0-or-later WITH Texinfo-exception
|
||||
Description : The glibc package contains standard libraries which are used by
|
||||
: multiple programs on the system. In order to save disk space and
|
||||
: memory, as well as to make upgrading easier, common system code is
|
||||
: kept in one place and shared between programs. This particular package
|
||||
: contains the most important sets of shared libraries: the standard C
|
||||
: library and the standard math library. Without these two libraries, a
|
||||
: Linux system will not function.
|
||||
@@ -0,0 +1,13 @@
|
||||
Installed Packages
|
||||
Name : su-dummy-a
|
||||
Version : 1.0
|
||||
Release : 1
|
||||
Architecture : noarch
|
||||
Size : 15
|
||||
Source : su-dummy-a-1.0-1.src.rpm
|
||||
Repository : @System
|
||||
Summary : skeptical-updater dummy su-dummy-a (v1.0)
|
||||
License : MIT
|
||||
Description : Dummy package su-dummy-a version 1.0 for skeptical-updater tests.
|
||||
:
|
||||
: Second paragraph of su-dummy-a 1.0 (blank line above on purpose).
|
||||
@@ -0,0 +1,13 @@
|
||||
Installed Packages
|
||||
Name : su-dummy-b
|
||||
Version : 1.0
|
||||
Release : 1
|
||||
Architecture : noarch
|
||||
Size : 15
|
||||
Source : su-dummy-b-1.0-1.src.rpm
|
||||
Repository : @System
|
||||
Summary : skeptical-updater dummy su-dummy-b (v1.0)
|
||||
License : MIT
|
||||
Description : Dummy package su-dummy-b version 1.0 for skeptical-updater tests.
|
||||
:
|
||||
: Second paragraph of su-dummy-b 1.0 (blank line above on purpose).
|
||||
@@ -0,0 +1,16 @@
|
||||
Installed packages
|
||||
Name : su-dummy-b
|
||||
Epoch : 0
|
||||
Version : 1.0
|
||||
Release : 1
|
||||
Architecture : noarch
|
||||
Installed size : 15.0 B
|
||||
Source : su-dummy-b-1.0-1.src.rpm
|
||||
From repository : <unknown>
|
||||
Summary : skeptical-updater dummy su-dummy-b (v1.0)
|
||||
URL :
|
||||
License : MIT
|
||||
Description : Dummy package su-dummy-b version 1.0 for skeptical-updater tests.
|
||||
:
|
||||
: Second paragraph of su-dummy-b 1.0 (blank line above on purpose).
|
||||
Vendor : <NULL>
|
||||
@@ -0,0 +1,18 @@
|
||||
Available Upgrades
|
||||
Name : glibc
|
||||
Version : 2.34
|
||||
Release : 283.el9_8
|
||||
Architecture : x86_64
|
||||
Size : 2.0 M
|
||||
Source : glibc-2.34-283.el9_8.src.rpm
|
||||
Repository : baseos
|
||||
Summary : The GNU libc libraries
|
||||
URL : http://www.gnu.org/software/glibc/
|
||||
License : LGPL-2.1-or-later AND SunPro AND LGPL-2.1-or-later WITH GCC-exception-2.0 AND BSD-3-Clause AND GPL-2.0-or-later AND LGPL-2.1-or-later WITH GNU-compiler-exception AND GPL-2.0-only AND ISC AND LicenseRef-Fedora-Public-Domain AND HPND AND CMU-Mach AND LGPL-2.0-or-later AND Unicode-3.0 AND GFDL-1.1-or-later AND GPL-1.0-or-later AND FSFUL AND MIT AND Inner-Net-2.0 AND X11 AND GPL-2.0-or-later WITH GCC-exception-2.0 AND GFDL-1.3-only AND GFDL-1.1-only AND GPL-3.0-or-later AND GPL-3.0-or-later WITH Autoconf-exception-generic-3.0 AND GPL-3.0-or-later WITH Texinfo-exception
|
||||
Description : The glibc package contains standard libraries which are used by
|
||||
: multiple programs on the system. In order to save disk space and
|
||||
: memory, as well as to make upgrading easier, common system code is
|
||||
: kept in one place and shared between programs. This particular package
|
||||
: contains the most important sets of shared libraries: the standard C
|
||||
: library and the standard math library. Without these two libraries, a
|
||||
: Linux system will not function.
|
||||
@@ -0,0 +1,13 @@
|
||||
Available Upgrades
|
||||
Name : su-dummy-a
|
||||
Version : 2.0
|
||||
Release : 1
|
||||
Architecture : noarch
|
||||
Size : 6.3 k
|
||||
Source : su-dummy-a-2.0-1.src.rpm
|
||||
Repository : su-dummy
|
||||
Summary : skeptical-updater dummy su-dummy-a (v2.0)
|
||||
License : MIT
|
||||
Description : Dummy package su-dummy-a version 2.0 for skeptical-updater tests.
|
||||
:
|
||||
: Second paragraph of su-dummy-a 2.0 (blank line above on purpose).
|
||||
@@ -0,0 +1,13 @@
|
||||
Available Upgrades
|
||||
Name : su-dummy-b
|
||||
Version : 2.0
|
||||
Release : 1
|
||||
Architecture : noarch
|
||||
Size : 6.5 k
|
||||
Source : su-dummy-b-2.0-1.src.rpm
|
||||
Repository : su-dummy
|
||||
Summary : skeptical-updater dummy su-dummy-b (v2.0)
|
||||
License : MIT
|
||||
Description : Dummy package su-dummy-b version 2.0 for skeptical-updater tests.
|
||||
:
|
||||
: Second paragraph of su-dummy-b 2.0 (blank line above on purpose).
|
||||
@@ -0,0 +1,17 @@
|
||||
Available upgrades
|
||||
Name : su-dummy-b
|
||||
Epoch : 0
|
||||
Version : 2.0
|
||||
Release : 1
|
||||
Architecture : noarch
|
||||
Download size : 6.6 KiB
|
||||
Installed size : 15.0 B
|
||||
Source : su-dummy-b-2.0-1.src.rpm
|
||||
Repository : su-dummy
|
||||
Summary : skeptical-updater dummy su-dummy-b (v2.0)
|
||||
URL :
|
||||
License : MIT
|
||||
Description : Dummy package su-dummy-b version 2.0 for skeptical-updater tests.
|
||||
:
|
||||
: Second paragraph of su-dummy-b 2.0 (blank line above on purpose).
|
||||
Vendor :
|
||||
@@ -264,3 +264,27 @@ Install Reason : Explicitly installed
|
||||
Install Script : No
|
||||
Validated By : None
|
||||
|
||||
|
||||
Name : glib2
|
||||
Version : 2.88.3-1
|
||||
Description : Low level core library
|
||||
Architecture : x86_64
|
||||
URL : https://gitlab.gnome.org/GNOME/glib
|
||||
Licenses : LGPL-2.1-or-later
|
||||
Groups : None
|
||||
Provides : libglib-2.0.so=0-64 libgio-2.0.so=0-64 libgirepository-2.0.so=0-64 libgmodule-2.0.so=0-64 libgobject-2.0.so=0-64 libgthread-2.0.so=0-64
|
||||
Depends On : bash glibc libffi libsysprof-capture pcre2 util-linux-libs zlib libffi.so=8-64 libmount.so=1-64
|
||||
Optional Deps : dconf: GSettings storage backend
|
||||
glib2-devel: development tools
|
||||
gvfs: most gio functionality
|
||||
Required By : libsecret pinentry
|
||||
Optional For : None
|
||||
Conflicts With : None
|
||||
Replaces : None
|
||||
Installed Size : 37.89 MiB
|
||||
Packager : Jan Alexander Steffens (heftig) <heftig@archlinux.org>
|
||||
Build Date : Wed Jul 29 14:57:16 2026
|
||||
Install Date : Sun Oct 4 00:00:00 2026
|
||||
Install Reason : Installed as a dependency for another package
|
||||
Install Script : No
|
||||
Validated By : Signature
|
||||
@@ -93,3 +93,25 @@ Packager : su-fixture <su-fixture@example.invalid>
|
||||
Build Date : Tue Nov 14 22:13:20 2023
|
||||
Validated By : None
|
||||
|
||||
|
||||
Repository : core
|
||||
Name : glib2
|
||||
Version : 2.90.1-1
|
||||
Description : Low level core library
|
||||
Architecture : x86_64
|
||||
URL : https://gitlab.gnome.org/GNOME/glib
|
||||
Licenses : LGPL-2.1-or-later
|
||||
Groups : None
|
||||
Provides : libglib-2.0.so=0-64 libgio-2.0.so=0-64 libgirepository-2.0.so=0-64 libgmodule-2.0.so=0-64 libgobject-2.0.so=0-64 libgthread-2.0.so=0-64
|
||||
Depends On : bash glibc libffi libsysprof-capture pcre2 util-linux-libs zlib libffi.so=8-64 libmount.so=1-64
|
||||
Optional Deps : dconf: GSettings storage backend
|
||||
glib2-devel: development tools
|
||||
gvfs: most gio functionality
|
||||
python: gdbus-codegen
|
||||
Conflicts With : None
|
||||
Replaces : None
|
||||
Download Size : 5.12 MiB
|
||||
Installed Size : 38.39 MiB
|
||||
Packager : Jan Alexander Steffens (heftig) <heftig@archlinux.org>
|
||||
Build Date : Tue Oct 6 16:19:12 2026
|
||||
Validated By : SHA-256 Sum Signature
|
||||
@@ -6,4 +6,5 @@ extra pacman-contrib 1.13.1-1
|
||||
su-dummy su-dummy-a 2.0-1 [installed: 1.0-1]
|
||||
su-dummy su-dummy-b 2.0-1 [installed: 1.0-1]
|
||||
su-dummy su-dummy-c 2.0-1 [installed: 1.0-1]
|
||||
su-dummy su-dummy-new 1.0-1
|
||||
su-dummy2 su-dummy-b 3.0-1
|
||||
@@ -2,8 +2,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake dpkg-query (unit tests): the dpkg database dump used for dependency closures, and
|
||||
# `-W -f '${db:Status-Abbrev}' NAME` (the status of one package; purged = unknown to dpkg)
|
||||
# fake dpkg-query (unit tests): the dpkg database dump used for dependency closures,
|
||||
# `-W -f '${db:Status-Abbrev}' NAME` (the status of one package; purged = unknown to dpkg) and
|
||||
# `-s NAME` (dpkg's record: data/apt/status/NAME; none or purged = not installed)
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
fake_log "$@"
|
||||
@@ -21,6 +22,15 @@ while (($#)); do
|
||||
esac
|
||||
shift
|
||||
done
|
||||
if [[ $args == *' -s '* ]]; then
|
||||
n=${names[0]%:amd64}
|
||||
if [[ ! -f $FAKE_DATA/apt/status/$n ]] || fake_purged "$n"; then
|
||||
echo "dpkg-query: package '$n' is not installed and no information is available" >&2
|
||||
exit 1
|
||||
fi
|
||||
cat "$FAKE_DATA/apt/status/$n"
|
||||
exit 0
|
||||
fi
|
||||
if ((${#names[@]})); then
|
||||
rc=0
|
||||
for n in "${names[@]}"; do
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
# "Obsoleting Packages" section; FAKE_DNF_REMOVE_REFUSE=1 makes `--assumeno remove` fail;
|
||||
# removal tables: see the --assumeno remove branch. `repoquery --installed --qf %{reason}`: REASON of
|
||||
# data/rpm/db.bash (dnf5's words with FAKE_DNF_MAJOR=5), FAKE_DNF_REASON="pkg=raw value" overrides
|
||||
# it, FAKE_DNF_REPOQUERY_RC=N makes repoquery fail.
|
||||
# it, FAKE_DNF_REPOQUERY_RC=N makes repoquery fail. `info --installed|--upgrades|--available PKG`
|
||||
# replays data/dnf/info-<which>-PKG-dnf<major>.txt (none: no match); FAKE_DNF_INFO_TWICE=<which>
|
||||
# prints that block twice (two installed architectures, ...). Plain `info PKG`: data/dnf/info-PKG.txt.
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
# shellcheck source=../../data/rpm/db.bash
|
||||
@@ -72,10 +74,23 @@ case " $* " in
|
||||
;;
|
||||
*' info '*)
|
||||
p=${pos[-1]}
|
||||
if [[ -f $D/info-$p.txt ]]; then cat "$D/info-$p.txt"; else
|
||||
echo "Error: No matching Packages to list" >&2
|
||||
f=$D/info-$p.txt
|
||||
w=''
|
||||
case " $* " in
|
||||
*' --installed '*) w=installed ;;
|
||||
*' --upgrades '*) w=upgrades ;;
|
||||
*' --available '*) w=available ;;
|
||||
esac
|
||||
[[ -z $w ]] || f=$D/info-$w-$p-dnf$major.txt
|
||||
if [[ ! -f $f ]] || { [[ $w == installed ]] && fake_purged "$p"; }; then
|
||||
if ((major == 5)); then echo "No matching packages to list" >&2; else echo "Error: No matching Packages to list" >&2; fi
|
||||
exit 1
|
||||
fi
|
||||
cat "$f"
|
||||
if [[ -n $w && ${FAKE_DNF_INFO_TWICE:-} == "$w" ]]; then
|
||||
echo
|
||||
sed '1d' "$f"
|
||||
fi
|
||||
;;
|
||||
*' --assumeno '*remove*)
|
||||
# the removal table: the target, its dependents (su-dummy-a for su-dummy-b; FAKE_DNF_DEPENDENT=
|
||||
|
||||
@@ -20,23 +20,26 @@
|
||||
fake_log "$@"
|
||||
D=$FAKE_DATA/pacman
|
||||
|
||||
# blocks FILE NAME...: print the blank-line separated blocks whose Name is one of NAME
|
||||
# (all blocks when no NAME); exit 1 (after printing the others) if a NAME is missing
|
||||
# blocks FILE NAME...: print the blank-line separated blocks whose Name is one of NAME, or whose
|
||||
# Repository/Name is (-Si repo/name); all blocks when no NAME; exit 1 (after printing the others)
|
||||
# if a NAME is missing
|
||||
blocks() {
|
||||
local f=$1 line block='' name='' rc=0 n
|
||||
local f=$1 line block='' name='' repo='' rc=0 n
|
||||
shift
|
||||
local -A want=() found=()
|
||||
for n in "$@"; do want[$n]=1; done
|
||||
while IFS= read -r line || [[ -n $block ]]; do
|
||||
if [[ -z $line ]]; then
|
||||
if [[ -n $block ]] && { (($# == 0)) || [[ -n ${want[$name]:-} ]]; } && ! fake_purged "$name"; then
|
||||
if [[ -n $block ]] && { (($# == 0)) || [[ -n ${want[$name]:-} || -n ${want[$repo/$name]:-} ]]; } && ! fake_purged "$name"; then
|
||||
printf '%s\n' "$block"
|
||||
found[$name]=1
|
||||
found[$repo/$name]=1
|
||||
fi
|
||||
block='' name=''
|
||||
block='' name='' repo=''
|
||||
continue
|
||||
fi
|
||||
[[ $line =~ ^Name\ +:\ (.*)$ ]] && name=${BASH_REMATCH[1]}
|
||||
[[ $line =~ ^Repository\ +:\ (.*)$ ]] && repo=${BASH_REMATCH[1]}
|
||||
block+=$line$'\n'
|
||||
done <"$f"
|
||||
for n in "$@"; do
|
||||
@@ -121,11 +124,7 @@ case $op in
|
||||
{ print }' <<<"$out"
|
||||
exit "$rc"
|
||||
;;
|
||||
-Si)
|
||||
t=()
|
||||
for a in "${targets[@]}"; do t+=("${a#*/}"); done
|
||||
blocks "$D/Si.txt" "${t[@]}"
|
||||
;;
|
||||
-Si) blocks "$D/Si.txt" "${targets[@]}" ;;
|
||||
-Su | -Syu)
|
||||
echo "real sync at $op: $(cat "$FAKE_PACMAN_DBPATH/sync/su-dummy.db" 2>/dev/null || echo none) / $(cat "$FAKE_PACMAN_DBPATH/sync/su-dummy.db.sig" 2>/dev/null || echo no-sig)" >>"$FAKE_LOG"
|
||||
if [[ -e $FAKE_PACMAN_DBPATH/db.lck ]]; then echo "db.lck at $op: yes" >>"$FAKE_LOG"; else echo "db.lck at $op: no" >>"$FAKE_LOG"; fi
|
||||
|
||||
@@ -90,14 +90,25 @@ UPGRADE='apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--forc
|
||||
log_lacks '^dpkg-query -W -f \$\{db:Status-Abbrev\} '
|
||||
}
|
||||
|
||||
@test "N?? prints the native full info" {
|
||||
@test "N?? prints the installed record merged with the candidate" {
|
||||
run skup --no-refresh <<<$'1??\nq'
|
||||
assert_success
|
||||
assert_line '# lines starting with > come from the upstream candidate: a differing value, or a field the installed record lacks'
|
||||
assert_line 'Package: su-dummy-a'
|
||||
assert_line 'Depends: su-dummy-b (>= 2.0)'
|
||||
assert_line 'Version: 1.0'
|
||||
assert_line '> Version: 2.0'
|
||||
assert_line '> Depends: su-dummy-b (>= 2.0)'
|
||||
log_has 'dpkg-query -s -- su-dummy-a'
|
||||
log_has 'apt-cache show --no-all-versions su-dummy-a'
|
||||
}
|
||||
|
||||
@test "N?? on a new dependency (old version -) never looks for an installed record" {
|
||||
FAKE_APT_SIM=real run skup --no-refresh <<<$'9??\nq'
|
||||
assert_success
|
||||
log_lacks '^dpkg-query -s'
|
||||
log_has 'apt-cache show --no-all-versions su-dummy-newdep'
|
||||
}
|
||||
|
||||
@test "N- toggles the exclusion and the '-' marker" {
|
||||
run skup --no-refresh <<<$'2-\nl\n2-\nl\n2-\n'
|
||||
assert_success
|
||||
|
||||
+208
-14
@@ -111,17 +111,70 @@ setup() { common_setup; }
|
||||
assert_line 'no-such-pkg-xyz: (no description available: unknown package?)'
|
||||
}
|
||||
|
||||
@test "apt: show is apt-cache show --no-all-versions (the candidate)" {
|
||||
@test "apt: show merges dpkg's installed record (dpkg-query -s) with the candidate (apt-cache show)" {
|
||||
# changed Version, local-only Status/Conffiles, identical fields once, a differing multi-line
|
||||
# Description (prefixed on every line), candidate-only fields appended
|
||||
run skup show su-dummy-b
|
||||
assert_success
|
||||
assert_output "$(cat <<'EOF'
|
||||
# lines starting with > come from the upstream candidate: a differing value, or a field the installed record lacks
|
||||
Package: su-dummy-b
|
||||
Status: install ok installed
|
||||
Priority: optional
|
||||
Section: misc
|
||||
Maintainer: Skeptical Updater Fixture <su-fixture@example.invalid>
|
||||
Architecture: all
|
||||
Version: 1.0
|
||||
> Version: 2.0
|
||||
Conffiles:
|
||||
/etc/su-dummy/su-dummy-b.conf 607e2d3a119dfe832d937ce579ef1079
|
||||
Description: dummy package su-dummy-b for skeptical-updater tests
|
||||
This package only exists to exercise the package manager.
|
||||
.
|
||||
It ships one data file and one conffile (version 1.0).
|
||||
> Description: dummy package su-dummy-b for skeptical-updater tests
|
||||
> This package only exists to exercise the package manager.
|
||||
> .
|
||||
> It ships one data file and one conffile (version 2.0).
|
||||
> Description-md5: 36da7dcdaf4ec207be2937d1f5dfa408
|
||||
> Filename: ./su-dummy-b_2.0_all.deb
|
||||
> Size: 840
|
||||
EOF
|
||||
)"
|
||||
log_has 'dpkg-query -s -- su-dummy-b'
|
||||
log_has 'apt-cache show --no-all-versions su-dummy-b'
|
||||
run skup show su-dummy-a
|
||||
assert_success
|
||||
assert_line 'Package: su-dummy-a'
|
||||
assert_line 'Version: 2.0'
|
||||
assert_line 'Depends: su-dummy-b (>= 2.0)'
|
||||
assert_line 'Depends: su-dummy-b'
|
||||
assert_line '> Depends: su-dummy-b (>= 2.0)'
|
||||
run skup show no-such-pkg-xyz
|
||||
assert_failure 1
|
||||
assert_output --partial "no package information for 'no-such-pkg-xyz' (apt exited 100)"
|
||||
}
|
||||
|
||||
@test "apt: a one-line candidate Description (the archives' short form) equals the installed long one" {
|
||||
# real debian:12 records: perl-base 5.36.0-7+deb12u3 installed, +deb12u4 from bookworm-security
|
||||
run skup show perl-base
|
||||
assert_success
|
||||
[[ $(grep -c '^Description: minimal Perl system$' <<<"$output") -eq 1 ]]
|
||||
refute_line --partial '> Description:'
|
||||
assert_output --partial "$(printf '%s\n' 'Installed-Size: 7639' '> Installed-Size: 7643')"
|
||||
assert_output --partial "$(printf '%s\n' 'Version: 5.36.0-7+deb12u3' '> Version: 5.36.0-7+deb12u4')"
|
||||
[[ $(grep -c '^Breaks: ' <<<"$output") -eq 1 ]]
|
||||
refute_line --regexp '^> (Breaks|Pre-Depends|Essential|Source):'
|
||||
assert_line --index -1 '> SHA256: d7d1943aec9597629bf73075efcc5ef6dc9bda96d78e80d843156ecd448478b8'
|
||||
}
|
||||
|
||||
@test "apt: show of a package that is not installed prints the candidate alone, with a note" {
|
||||
run skup show zsh
|
||||
assert_success
|
||||
assert_line --index 0 '# not installed: this is the upstream candidate'
|
||||
assert_line --index 1 'Package: zsh'
|
||||
assert_line --index 3 'Version: 5.9-4+b15'
|
||||
refute_line --partial '> '
|
||||
log_has 'dpkg-query -s -- zsh'
|
||||
}
|
||||
|
||||
# ---- pacman -------------------------------------------------------------------------------------
|
||||
|
||||
@test "pacman: -Qu joined with the FIRST -Sl repo; [ignored] => held" {
|
||||
@@ -219,15 +272,73 @@ setup() { common_setup; }
|
||||
assert_output 'trusted su-dummy-a (+1 dependency: su-dummy-b)'
|
||||
}
|
||||
|
||||
@test "pacman: show prints the installed (-Qi) and the candidate (-Si) records" {
|
||||
@test "pacman: show merges -Qi with -Si of the candidate's repository: '>' lines for what differs" {
|
||||
# real archlinux records (glib2 2.88.3-1 installed, core 2.90.1-1), one Optional Deps entry
|
||||
# added to the candidate: changed fields, identical ones once, -Qi-only fields kept, a differing
|
||||
# multi-line block, -Si-only fields appended
|
||||
use_fakes pacman
|
||||
run skup show su-dummy-a
|
||||
run skup show glib2
|
||||
assert_success
|
||||
assert_line 'Install Reason : Explicitly installed'
|
||||
assert_line 'Repository : su-dummy'
|
||||
assert_line 'Version : 2.0-1'
|
||||
log_has 'pacman -Qi -- su-dummy-a'
|
||||
log_has 'pacman -Si -- su-dummy-a'
|
||||
assert_output "$(cat <<'EOF'
|
||||
# lines starting with > come from the upstream candidate: a differing value, or a field the installed record lacks
|
||||
Name : glib2
|
||||
Version : 2.88.3-1
|
||||
> Version : 2.90.1-1
|
||||
Description : Low level core library
|
||||
Architecture : x86_64
|
||||
URL : https://gitlab.gnome.org/GNOME/glib
|
||||
Licenses : LGPL-2.1-or-later
|
||||
Groups : None
|
||||
Provides : libglib-2.0.so=0-64 libgio-2.0.so=0-64 libgirepository-2.0.so=0-64 libgmodule-2.0.so=0-64 libgobject-2.0.so=0-64 libgthread-2.0.so=0-64
|
||||
Depends On : bash glibc libffi libsysprof-capture pcre2 util-linux-libs zlib libffi.so=8-64 libmount.so=1-64
|
||||
Optional Deps : dconf: GSettings storage backend
|
||||
glib2-devel: development tools
|
||||
gvfs: most gio functionality
|
||||
> Optional Deps : dconf: GSettings storage backend
|
||||
> glib2-devel: development tools
|
||||
> gvfs: most gio functionality
|
||||
> python: gdbus-codegen
|
||||
Required By : libsecret pinentry
|
||||
Optional For : None
|
||||
Conflicts With : None
|
||||
Replaces : None
|
||||
Installed Size : 37.89 MiB
|
||||
> Installed Size : 38.39 MiB
|
||||
Packager : Jan Alexander Steffens (heftig) <heftig@archlinux.org>
|
||||
Build Date : Wed Jul 29 14:57:16 2026
|
||||
> Build Date : Tue Oct 6 16:19:12 2026
|
||||
Install Date : Sun Oct 4 00:00:00 2026
|
||||
Install Reason : Installed as a dependency for another package
|
||||
Install Script : No
|
||||
Validated By : Signature
|
||||
> Validated By : SHA-256 Sum Signature
|
||||
> Repository : core
|
||||
> Download Size : 5.12 MiB
|
||||
EOF
|
||||
)"
|
||||
log_has 'pacman -Qi -- glib2'
|
||||
log_has 'pacman -Si -- core/glib2'
|
||||
}
|
||||
|
||||
@test "pacman: the candidate comes from the first repository -Sl lists; no -Si record = the plain -Qi" {
|
||||
use_fakes pacman
|
||||
run skup show su-dummy-b
|
||||
assert_success
|
||||
assert_output --partial "$(printf '%s\n' 'Version : 1.0-1' '> Version : 2.0-1')"
|
||||
assert_line 'Required By : su-dummy-a'
|
||||
refute_output --partial '3.0-1'
|
||||
log_has 'pacman -Si -- su-dummy/su-dummy-b'
|
||||
# bash: in the sync list, but no -Si record - the output stays what it was (-Qi, then -Si)
|
||||
run skup show bash
|
||||
assert_success
|
||||
assert_line --index 0 'Name : bash'
|
||||
refute_line --partial '# lines starting with >'
|
||||
# not installed: the candidate alone
|
||||
run skup show su-dummy-new
|
||||
assert_success
|
||||
assert_line --index 0 '# not installed: this is the upstream candidate'
|
||||
assert_line --index 1 'Repository : su-dummy'
|
||||
refute_line --partial '> '
|
||||
run skup show nope
|
||||
assert_failure 1
|
||||
}
|
||||
@@ -238,6 +349,7 @@ setup() { common_setup; }
|
||||
run skup info glibc
|
||||
run skup list --no-refresh
|
||||
run skup trust bash
|
||||
run skup show glib2
|
||||
run grep -v ' 0$' "$FAKE_DIR/pacman-columns"
|
||||
assert_output ''
|
||||
}
|
||||
@@ -391,12 +503,93 @@ setup() { common_setup; }
|
||||
log_has 'dnf -y remove su-dummy-a su-dummy-b'
|
||||
}
|
||||
|
||||
@test "dnf: show is dnf -q info" {
|
||||
@test "dnf4: show merges info --installed with info --upgrades (headings dropped)" {
|
||||
use_fakes dnf rpm
|
||||
run skup show su-dummy-a
|
||||
run skup show su-dummy-b
|
||||
assert_success
|
||||
assert_output "$(cat <<'EOF'
|
||||
# lines starting with > come from the upstream candidate: a differing value, or a field the installed record lacks
|
||||
Name : su-dummy-b
|
||||
Version : 1.0
|
||||
> Version : 2.0
|
||||
Release : 1
|
||||
Architecture : noarch
|
||||
Size : 15
|
||||
> Size : 6.5 k
|
||||
Source : su-dummy-b-1.0-1.src.rpm
|
||||
> Source : su-dummy-b-2.0-1.src.rpm
|
||||
Repository : @System
|
||||
> Repository : su-dummy
|
||||
Summary : skeptical-updater dummy su-dummy-b (v1.0)
|
||||
> Summary : skeptical-updater dummy su-dummy-b (v2.0)
|
||||
License : MIT
|
||||
Description : Dummy package su-dummy-b version 1.0 for skeptical-updater tests.
|
||||
:
|
||||
: Second paragraph of su-dummy-b 1.0 (blank line above on purpose).
|
||||
> Description : Dummy package su-dummy-b version 2.0 for skeptical-updater tests.
|
||||
> :
|
||||
> : Second paragraph of su-dummy-b 2.0 (blank line above on purpose).
|
||||
EOF
|
||||
)"
|
||||
log_has 'dnf -q info --installed su-dummy-b'
|
||||
log_has 'dnf -q info --upgrades su-dummy-b'
|
||||
# real almalinux:9 records: an identical multi-line Description once, "From repo" (installed only) kept
|
||||
run skup show glibc
|
||||
assert_success
|
||||
assert_output --partial "$(printf '%s\n' 'Release : 275.el9_8' '> Release : 283.el9_8')"
|
||||
assert_line 'From repo : baseos'
|
||||
[[ $(grep -c '^Description : The glibc package contains' <<<"$output") -eq 1 ]]
|
||||
[[ $(grep -c 'Linux system will not function\.$' <<<"$output") -eq 1 ]]
|
||||
}
|
||||
|
||||
@test "dnf5: show merges records whose keys are padded differently (re-padded, extra fields appended)" {
|
||||
use_fakes dnf rpm
|
||||
FAKE_DNF_MAJOR=5 run skup show su-dummy-b
|
||||
assert_success
|
||||
assert_output "$(cat <<'EOF'
|
||||
# lines starting with > come from the upstream candidate: a differing value, or a field the installed record lacks
|
||||
Name : su-dummy-b
|
||||
Epoch : 0
|
||||
Version : 1.0
|
||||
> Version : 2.0
|
||||
Release : 1
|
||||
Architecture : noarch
|
||||
Installed size : 15.0 B
|
||||
Source : su-dummy-b-1.0-1.src.rpm
|
||||
> Source : su-dummy-b-2.0-1.src.rpm
|
||||
From repository : <unknown>
|
||||
Summary : skeptical-updater dummy su-dummy-b (v1.0)
|
||||
> Summary : skeptical-updater dummy su-dummy-b (v2.0)
|
||||
URL :
|
||||
License : MIT
|
||||
Description : Dummy package su-dummy-b version 1.0 for skeptical-updater tests.
|
||||
:
|
||||
: Second paragraph of su-dummy-b 1.0 (blank line above on purpose).
|
||||
> Description : Dummy package su-dummy-b version 2.0 for skeptical-updater tests.
|
||||
> :
|
||||
> : Second paragraph of su-dummy-b 2.0 (blank line above on purpose).
|
||||
Vendor : <NULL>
|
||||
> Vendor :
|
||||
> Download size : 6.6 KiB
|
||||
> Repository : su-dummy
|
||||
EOF
|
||||
)"
|
||||
}
|
||||
|
||||
@test "dnf: not installed = info --available alone; not one record per side = the plain dnf -q info" {
|
||||
use_fakes dnf rpm
|
||||
run skup show zsh
|
||||
assert_success
|
||||
assert_line --index 0 '# not installed: this is the upstream candidate'
|
||||
assert_line --index 1 'Name : zsh'
|
||||
refute_line 'Available Packages'
|
||||
log_has 'dnf -q info --available zsh'
|
||||
# two installed records (e.g. two architectures): nothing to pair, the output stays dnf -q info
|
||||
FAKE_DNF_INFO_TWICE=installed run skup show su-dummy-a
|
||||
assert_success
|
||||
refute_line --partial '# lines starting with >'
|
||||
assert_line 'Installed Packages'
|
||||
assert_line 'Available Upgrades'
|
||||
assert_line 'Version : 2.0'
|
||||
log_has 'dnf -q info su-dummy-a'
|
||||
}
|
||||
|
||||
@@ -481,6 +674,7 @@ setup() { common_setup; }
|
||||
run skup show su-dummy-a
|
||||
assert_success
|
||||
assert_line 'Information for package su-dummy-a:'
|
||||
refute_line --partial '# lines starting with >'
|
||||
log_has 'zypper --non-interactive --quiet info --requires su-dummy-a'
|
||||
run skup show nothing-like-this
|
||||
assert_failure 1
|
||||
|
||||
Reference in new issue
Block a user