diff --git a/README.md b/README.md index 911430a..8b267fa 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,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: the installed record, with the upstream candidate merged in as lines starting with `>` (see below). zypper shows `zypper info --requires` as it is | +| `N??` | full native info: the installed record, with the upstream candidate merged in as lines starting with `*` (see below); every line has a two-character gutter (` ` installed, `* ` upstream). 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) | @@ -153,43 +153,52 @@ 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: +it. Every other line starts with a two-character gutter, so keys and colons line up: two spaces +for the installed record, `* ` for the upstream candidate. 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, marked `* `; fields only the installed record +has (install reason, conffiles, `Required By`) stay as they are, and identical fields are printed +once. Fields only the candidate has (download size, checksums, repository) follow at the end the same +way - on pacman next to their installed counterpart instead (`Repository` after `Version`, +`Download Size` after `Installed Size`; for the AUR `AUR URL` after `URL`, `Make Deps`/`Check Deps` +after `Optional Deps`, `Maintainer` after `Packager`, `Last Modified` and `First Submitted` after +`Build Date`, the rest at the end). Multi-line values (descriptions, apt continuation lines) are +compared as blocks and, when they differ, every line of the candidate's block gets the `* `. On pacman +values are compared unwrapped (the AUR helpers wrap long lists at the terminal width), lists such as +`Depends On` as sets of tokens in any order, `Optional Deps` by package names only (the installed +record says `name: description [installed]`, the AUR bare names); a differing value is printed on one +line like `pacman -Qi` does, `Optional Deps` one entry per line. A package that is not installed shows +the candidate alone, every line marked `* `. 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, in the same gutter. 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 -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 +# 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 + 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 @@ -241,8 +250,9 @@ Upgrade finished. AUR row (a package that moved from the AUR into a repository). - **`N?`, `N+`, `Nx`** work as for any installed package, through pacman (`pacman -Qi`, the statefile with backend `pacman`, `pacman -Rns`). **`N??`** merges `pacman -Qi` with the - AUR record (`paru -Sia` / `yay -Sia`), `>` lines as usual: `> Version`, `> Repository : aur`, - `> Maintainer`, `> Votes`, ... (`show PKG` does the same for a package in no repository). + AUR record (`paru -Sia` / `yay -Sia`), `*` lines as usual: `* Version` followed by + `* Repository : aur`, `* AUR URL` under `URL`, `* Maintainer` under `Packager`, `* Votes`, ... + (`show PKG` does the same for a package in no repository). - **`N-`** excludes an AUR row through the helper's `--ignore`. - **``** upgrades the repository packages first, exactly as without the AUR (pre-check, the reviewed databases, `pacman -Su`), then, if AUR rows are left to upgrade, runs @@ -398,7 +408,9 @@ database install / `-Su` sequence and stale throwaway databases, statefile locki 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), 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), the prompt on a real pty (`script`): +short candidate descriptions, dnf5's padding, the fallbacks, the two-character gutter; on pacman the +unwrapped comparison, token sets, `Optional Deps` name sets and the placement of candidate-only +fields, on the recorded `claude-desktop` records of paru and of yay, which wraps), the prompt on a real pty (`script`): cursor editing, Up/Down history recall, the stashed line, the bell at both ends, no history file, and the AUR (`tests/unit/aur.bats`, on recorded paru 2.1.0 and yay 13.0.1 output): helper detection and overrides, the user the helper runs as (`runuser -u` / `sudo -u` with `SUDO_USER`, unprivileged, @@ -422,8 +434,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??` (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, +reason and `required by:` of both), `N??` (su-dummy-b's installed 1.0 record with `* Version`, and +`* Repository` on pacman and dnf, every line in the gutter; 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 diff --git a/discovery/pacman.md b/discovery/pacman.md index d1034ed..8b3ab26 100644 --- a/discovery/pacman.md +++ b/discovery/pacman.md @@ -713,6 +713,33 @@ Out Of Date : No Submitted`, `Last Modified`, `Out Of Date` (yay: `Out-of-date`, plus `Keywords`). * Not in the AUR: paru `error: package 'no-such-package-zzz' was not found`, yay ` -> No AUR package found for no-such-package-zzz`, exit 1. AUR unreachable: the `-Qua` errors above, exit 1. +* Optional dependencies are **bare names** in both (`Optional Deps : pipewire kdialog gnome-keyring kwallet` + for `google-chrome`, whose AUR entries carry descriptions such as `pipewire: WebRTC desktop sharing under + Wayland`): the descriptions are dropped. `-Qi` prints one `name: description [installed]` per line. +* **yay wraps** long values at 80 columns - `Depends On`, `Optional Deps`, continuation lines indented to the value + column (31) - whenever `COLUMNS` is unset or 80, also without any terminal (stdout and stdin not a tty); + `COLUMNS=0` turns it off. paru 2.1.0 printed one line per field in every case tried (no terminal; an 80-column + pty with stdout piped, `COLUMNS` unset, 0 or 80; stdout the pty itself with `COLUMNS=0`). Verified 2026-10-10 + with `claude-desktop` 2.31226.1-1 (39 dependencies), `yay -Sia claude-desktop` without `COLUMNS=0`: + + ``` + Depends On : alsa-lib at-spi2-core bubblewrap cairo dbus + expat gcc-libs glib2 glibc gtk3 + ... + xdg-desktop-portal xdg-utils qemu-system-x86 + edk2-ovmf + Optional Deps : gnome-keyring kwallet libayatana-appindicator + xdg-desktop-portal-gtk xdg-desktop-portal-kde + qemu-system-aarch64 edk2-aarch64 + ``` + + (paru: `Depends On : alsa-lib at-spi2-core bubblewrap ... edk2-ovmf` on one line.) The installed side for + that comparison, `pacman -Qi claude-desktop` of 2.9939.4-1 (a stand-in package built from the AUR's own + `.SRCINFO` of that version, commit b7d5715, plus a dummy `kwallet` for `[installed]`): `Depends On` one line + without `bubblewrap`, `libpipewire`, `qemu-system-x86`, `edk2-ovmf`; `Optional Deps : gnome-keyring: + credential storage via Secret Service (GNOME)` / `kwallet: credential storage (KDE Plasma) [installed]` / ... + one per line. These records are the unit fixtures (`tests/unit/data/pacman/Qi.txt`, + `tests/unit/data/aur/Sia-{paru,yay}-claude-desktop.txt`). ### 15.3 `-Sua`: the AUR upgrade @@ -739,5 +766,6 @@ Out Of Date : No |------|---------| | helper | `--aur-helper=paru\|yay\|none` / `--no-aur`, else `paru`, else `yay` | | list | ` -Qua`: rows `name old new aur` (flag `aur`, `[ignored]` -> held); a name `pacman -Qu` lists wins; exit 1 + empty stderr = none, other failures: warn, no AUR rows | -| `N??` | `pacman -Qi` merged with ` -Sia PKG` (section 14's merge) | +| `N??` | `pacman -Qi` merged with ` -Sia PKG` (section 14's merge; pacman mode: values unwrapped, lists as token sets, `Optional Deps` as name sets, AUR-only fields next to their counterparts) | +| `COLUMNS` | `COLUMNS=0` for `-Qua` and `-Sia` (yay's wrapping); the upgrade keeps the terminal's | | upgrade | after the repository step: ` -Sua [--noconfirm when stdin is not a terminal] [--ignore ]` | diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 6729303..4a59f78 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -170,31 +170,62 @@ 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 +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` (no gutter); every other line +starts with a two-character gutter, ` ` for the installed record and its continuation lines, `* ` +for the candidate's lines and theirs, so keys and colons line up. 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, marked `* ` (same key formatting); fields only the candidate has are +appended at the end the same way (pacman: placed next to their counterpart, below); 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. +ones (dnf5: 16 installed, 15 available; yay's AUR records: 30), the candidate lines are re-padded to +the installed one's. + +pacman (`-Qi` with `-Si REPO/PKG` or ` -Sia PKG`, `discovery/pacman.md` sections 14-15): + +- values are compared unwrapped - continuation lines joined to their key on both sides, runs of + blanks as one blank - because the AUR helpers wrap long lists at the terminal width; +- `Groups`, `Provides`, `Depends On`, `Required By`, `Optional For`, `Conflicts With`, `Replaces`, + `Make Deps`, `Check Deps`, `Licenses`, `Keywords`: as sets of exact tokens, in any order (a version + constraint is part of its token, so a changed constraint is a difference); +- `Optional Deps`: as sets of package names - `: description` and ` [installed]` stripped (`-Qi` + prints `name: description [installed]` per line, the AUR records bare names separated by blanks); +- a differing candidate value is printed on one line, as `-Qi` prints every field (an unwrapped + helper value is joined with two blanks for the lists above, one otherwise; a one-line value stays + as it is); `Optional Deps`, one entry per line in `-Qi`, gets one entry per line under the value + column; +- candidate-only fields go next to their installed counterpart, in this order, the others at the end + in the candidate's order: + + | candidate field | right after | + |-----------------|-------------| + | `Repository` | `Version` | + | `AUR URL` | `URL` | + | `Keywords` | `Groups` | + | `Make Deps`, `Check Deps` | `Optional Deps` | + | `Maintainer` | `Packager` | + | `Last Modified`, `First Submitted` | `Build Date` | + | `Download Size` | `Installed Size` | | 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); a package in no repository: the AUR record, ` -Sia PKG`, when a helper is in use (should it not merge, `pacman -Qi` is followed by the AUR record with every line marked `> `) | +| pacman | `pacman -Qi PKG` | `pacman -Si REPO/PKG`, REPO = the first repository `pacman -Sl` lists PKG in (as for the review list); a package in no repository: the AUR record, ` -Sia PKG`, when a helper is in use (should it not merge, `pacman -Qi` is followed by the AUR record with every line marked `* `) | | apt | `dpkg-query -s PKG` (Status, Conffiles, always the long description; `apt-cache show PKG=` 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 +alone, every line marked `* `, under `# not installed: lines starting with * come from the upstream +candidate (all of them)`. 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 `_full_info` unchanged. `dnf info` headings +installed architectures), zypper - prints `_full_info` as it is (in the " " gutter). `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 diff --git a/skeptical-updater b/skeptical-updater index a0871ff..fe715ff 100755 --- a/skeptical-updater +++ b/skeptical-updater @@ -112,7 +112,7 @@ 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: the installed record; lines starting with '>' come + 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 @@ -137,7 +137,7 @@ session_help() { cat <<'EOF' upgrade now, skipping the rows marked '-' N? what is it: description, install reason + installed packages that need it - N?? full native info: installed record, '>' lines from the upstream candidate + 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 @@ -908,7 +908,7 @@ apt_describe() { [[ -n $out ]] || out='(no description available)' printf '%s\n' "$out" } -apt_full_info() { apt-cache show --no-all-versions "$1" `, 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 @@ -1292,9 +1292,11 @@ aur_warn_pending() { } # aur_exec ARGS...: run the helper (as the invoking user when we are root), stdin from /dev/null, -# our lock descriptors closed, from that user's home +# our lock descriptors closed, from that user's home; COLUMNS=0: yay wraps -Sia lists at 80 columns +# otherwise, even without a terminal (discovery/pacman.md 15.2; the merge unwraps anyway) aur_exec() { ( + export COLUMNS=0 if [[ -n $AUR_HOME ]]; then cd -- "$AUR_HOME" 2>/dev/null || cd /; fi run_child ${AUR_RUN[@]+"${AUR_RUN[@]}"} "$AUR_HELPER" "$@" ) @@ -1415,19 +1417,19 @@ pacman_describe() { [[ -n $out ]] || return 1 printf '%s\n' "$out" } -# pacman_full_info PKG: -Qi and -Si as pacman prints them (each record ends with a blank line). A -# package in no repository: the AUR record when a helper is in use, every line marked with "> " -# (reached only when it does not merge with -Qi - paru's and yay's records do) +# pacman_full_info PKG: -Qi and -Si as pacman prints them (each record ends with a blank line), with +# the " " gutter. A package in no repository: the AUR record when a helper is in use, every line +# marked "* " (reached only when it does not merge with -Qi - paru's and yay's records do) pacman_full_info() { - local ok=1 qi si aur='' l + local ok=1 qi si aur='' qi=$(_pm -Qi -- "$1" 2>/dev/null) && ok=0 si=$(_pm -Si -- "$1" 2>/dev/null) && ok=0 if [[ -z $si ]] && aur_prepare; then aur=$(aur_info "$1") || aur=''; fi - [[ -z $aur ]] || say "# lines starting with > are the AUR record ($AUR_HELPER -Sia) as it prints it" - [[ -z $qi ]] || printf '%s\n\n' "$qi" - [[ -z $si ]] || printf '%s\n\n' "$si" + [[ -z $aur ]] || say "# lines starting with * are the AUR record ($AUR_HELPER -Sia) as it prints it" + [[ -z $qi ]] || printf '%s\n\n' "$qi" | gutter + [[ -z $si ]] || printf '%s\n\n' "$si" | gutter if [[ -n $aur ]]; then - while IFS= read -r l; do printf '> %s\n' "$l"; done <<<"$aur" + printf '%s\n' "$aur" | sed '/./s/^/* /' ok=0 fi return "$ok" @@ -1866,7 +1868,7 @@ dnf_describe() { done <<<"$out" return 1 } -dnf_full_info() { "$DNF" -q info "$1" /dev/null) || return 1 printf '%s\n' "$out" } -zypper_full_info() { zypper --non-interactive --quiet info --requires "$1" _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. +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: lines starting with * come from the upstream candidate (all of them)' +# gutter: the two-character gutter of N?? / show for native output shown as it is (" " before every +# non-empty line; the merge itself uses " " for installed lines and "* " for the candidate's) +gutter() { sed '/./s/^/ /'; } +# merge: every line gets a two-character gutter: " " for the installed record, "* " for the +# candidate. Each installed field is printed as it is; when the candidate's value differs, the +# candidate's line(s) follow; fields only the candidate has are appended. 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 marked - 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, yay -Sia pads to 30), the candidate lines +# are re-padded to the installed record's column. +# pm=1 (pacman: -Qi with -Si or an AUR helper's -Sia; discovery/pacman.md sections 14-15): values are +# compared unwrapped (continuation lines joined: the AUR helpers wrap long lists at the terminal +# width), runs of blanks as one; list fields (Depends On, Provides, ...) as token sets, in any order +# (a version constraint is part of its token); Optional Deps by package names only (-Qi prints +# "name: description [installed]" per line, the AUR prints bare names). A differing candidate value +# is one line, as -Qi prints it - but Optional Deps, one entry per line under the value column - and +# candidate-only fields go next to their installed counterpart (the anchor table below: Repository +# after Version, AUR URL after URL, ...), the others at the end in the candidate's order. # shellcheck disable=SC2016 # awk program, not shell readonly AWK_MERGE_RECORDS=' function trim(s) { sub(/^[ \t]+/, "", s); sub(/[ \t\r]+$/, "", s); return s } @@ -2379,7 +2393,52 @@ function repad(l, i, k) { 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]) } +function mine(s, n, a, m) { n = split(s, a, "\n"); for (m = 1; m <= n; m++) print " " a[m] } +function theirs(s, n, a, m) { n = split(s, a, "\n"); for (m = 1; m <= n; m++) print "* " repad(a[m]) } +# kv K V: a "Key : value" line in the layout of the installed record +function kv(k, v) { return (col[1] > 0) ? sprintf("%-" (col[1] - 1) "s: %s", k, v) : k " : " v } +function isort(a, n, i, j, t) { for (i = 2; i <= n; i++) { t = a[i]; for (j = i - 1; j > 0 && a[j] > t; j--) a[j + 1] = a[j]; a[j + 1] = t } } +function joined(a, n, i, r) { r = ""; for (i = 1; i <= n; i++) r = r (i > 1 ? " " : "") a[i]; return r } +function flat(s) { gsub(/[ \t\n]+/, " ", s); return trim(s) } +# tokens S: the sorted tokens of a list value +function tokens(s, t, n, i, u, m) { n = split(flat(s), t, " "); m = 0; for (i = 1; i <= n; i++) if (t[i] != "") u[++m] = t[i]; isort(u, m); return joined(u, m) } +# entries S E: the entries of an Optional Deps value: one per line, or separated by 2+ blanks +function entries(s, e, n, a, m, c, b, x, ne) { + ne = 0; n = split(s, a, "\n") + for (m = 1; m <= n; m++) { c = split(a[m], b, /[ \t][ \t]+/); for (x = 1; x <= c; x++) { b[x] = trim(b[x]); if (b[x] != "") e[++ne] = b[x] } } + return ne +} +# names S: the sorted package names of an Optional Deps value ("None": none) +function names(s, e, n, i, x, u, m) { + n = entries(s, e); m = 0 + for (i = 1; i <= n; i++) { x = e[i]; sub(/[ \t]*\[installed\]$/, "", x); sub(/:( .*)?$/, "", x); x = trim(x); if (x != "" && x != "None") u[++m] = x } + isort(u, m); return joined(u, m) +} +function same(k) { + if (!pm) return val[1, k] == val[2, k] + if (k == "Optional Deps") return names(val[1, k]) == names(val[2, k]) + if (k in LIST) return tokens(val[1, k]) == tokens(val[2, k]) + return flat(val[1, k]) == flat(val[2, k]) +} +function up(k, e, n, i, ind, v) { + done[k] = 1 + if (!pm) { theirs(raw[2, k]); return } + if (k == "Optional Deps") { + n = entries(val[2, k], e); ind = (col[1] > 0) ? col[1] + 1 : length(k) + 3 + print "* " kv(k, e[1]) + for (i = 2; i <= n; i++) print "* " sprintf("%" ind "s", "") e[i] + return + } + if (nl[2, k] == 1) { theirs(raw[2, k]); return } + v = val[2, k]; gsub(/\n/, (k in LIST) ? " " : " ", v) + print "* " kv(k, v) +} +BEGIN { + n = split("Groups|Provides|Depends On|Required By|Optional For|Conflicts With|Replaces|Make Deps|Check Deps|Licenses|Keywords", t, "|") + for (i = 1; i <= n; i++) LIST[t[i]] = 1 + na = split("Repository>Version|AUR URL>URL|Keywords>Groups|Make Deps>Optional Deps|Check Deps>Optional Deps|Maintainer>Packager|Last Modified>Build Date|First Submitted>Build Date|Download Size>Installed Size", t, "|") + for (i = 1; i <= na; i++) { split(t[i], p, ">"); AK[i] = p[1]; AA[i] = p[2] } +} FNR == 1 { side = (FILENAME == ARGV[1]) ? 1 : 2; cur = "" } /^[ \t]*$/ { if (nk[side]) ended[side] = 1; next } /^[^ \t]/ { @@ -2405,23 +2464,23 @@ END { 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]) + mine(raw[1, k]) + if (((2, k) in raw) && !same(k) && !(apt && k == "Description" && nl[2, k] == 1 && val[2, k] == first[1, k])) up(k) + if (pm) for (a = 1; a <= na; a++) if (AA[a] == k && ((2, AK[a]) in raw) && !((1, AK[a]) in raw) && !(AK[a] in done)) up(AK[a]) } - for (j = 1; j <= nk[2]; j++) { k = key[2, j]; if (!((1, k) in raw)) pfx(raw[2, k]) } + for (j = 1; j <= nk[2]; j++) { k = key[2, j]; if (!((1, k) in raw) && !(k in done)) up(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 + local rc=0 inst='' cand='' out apt=0 pm=0 if declare -F "${BACKEND}_installed_record" >/dev/null; then [[ $BACKEND != apt ]] || apt=1 + [[ $BACKEND != pacman ]] || pm=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 + if [[ -n $cand ]] && out=$(awk -v apt="$apt" -v pm="$pm" "$AWK_MERGE_RECORDS" <(printf '%s\n' "$inst") <(printf '%s\n' "$cand")); then say "$SU_MERGE_NOTE" say "$out" return 0 @@ -2430,11 +2489,12 @@ print_full_info() { # print_full_info PKG [OLD] (N?? / show); 1 if the package m cand=$(be candidate_record "$1" not-installed) || cand='' if [[ -n $cand ]]; then say "$SU_CANDIDATE_NOTE" - say "$cand" + printf '%s\n' "$cand" | sed '/./s/^/* /' return 0 fi fi fi + # the native output as it is (_full_info prints it with the gutter) be full_info "$1" || rc=$? ((rc == 0)) && return 0 err "no package information for '$1' ($(be label) exited $rc)" diff --git a/tests/e2e/aur.bats b/tests/e2e/aur.bats index f6b04b2..48d9395 100644 --- a/tests/e2e/aur.bats +++ b/tests/e2e/aur.bats @@ -67,12 +67,12 @@ pacman_only() { [[ $BE == pacman ]] || skip "pacman backend only"; } 'su-dummy-aur: Skeptical-updater dummy package AUR (foreign: in no repository)' \ 'reason: explicitly installed' \ 'required by: nothing')" - assert_line '# lines starting with > come from the upstream candidate: a differing value, or a field the installed record lacks' - assert_output --partial "$(printf '%s\n' 'Version : 1.0-1' '> Version : 2.0-1')" - assert_line 'Install Reason : Explicitly installed' - assert_line '> Repository : aur' - assert_line '> Maintainer : su-fixture' - assert_line '> AUR URL : https://aur.archlinux.org/packages/su-dummy-aur' + assert_line '# lines starting with * come from the upstream candidate: a differing value, or a field the installed record lacks' + assert_output --partial "$(printf '%s\n' ' Version : 1.0-1' '* Version : 2.0-1' '* Repository : aur')" + assert_line ' Install Reason : Explicitly installed' + assert_output --partial "$(printf '%s\n' ' URL : https://example.invalid/su-dummy' '* AUR URL : https://aur.archlinux.org/packages/su-dummy-aur')" + assert_output --partial "$(printf '%s\n' ' Packager : su-fixture ' '* Maintainer : su-fixture')" + assert_output --partial "$(printf '%s\n' ' Validated By : None' '* Votes : 7' '* Popularity : 0.01' '* Out Of Date : No')" run grep -c 'su-tester HOME=/home/su-tester PWD=/home/su-tester -Sia su-dummy-aur' "$PARU_LOG" assert_output 1 } diff --git a/tests/e2e/skeptical-updater.bats b/tests/e2e/skeptical-updater.bats index 4561552..87488e0 100644 --- a/tests/e2e/skeptical-updater.bats +++ b/tests/e2e/skeptical-updater.bats @@ -60,29 +60,30 @@ zypper_verb() { case $(. /etc/os-release && printf '%s' "$ID") in opensuse-tumbl run skeptical-updater --no-refresh <<<$'2??\nq' assert_success 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 >' + 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' + 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$' + 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' + assert_output --partial "$(printf '%s\n' ' Version : 1.0-1' '* Version : 2.0-1' '* Repository : su-dummy')" + assert_line ' Install Reason : Installed as a dependency for another package' ;; dnf) - assert_output --partial "$(printf '%s\n' 'Version : 1.0' '> Version : 2.0')" - assert_output --partial "$(printf '%s\n' 'Repository : @System' '> Repository : su-dummy')" + 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 ]] + [[ $(grep -c '^\* ' <<<"$output") -ge 2 ]] + # every line has the two-character gutter, except the note + [[ $(grep -cvE '^(# lines starting with| |\* |$)' <<<"$(sed -n '/^# lines starting with/,/^> q$/p' <<<"$output" | sed '$d')") -eq 0 ]] } @test "N+ writes the user row and the dep row; list hides both and counts them" { diff --git a/tests/unit/aur.bats b/tests/unit/aur.bats index 2abaa29..975b08b 100644 --- a/tests/unit/aur.bats +++ b/tests/unit/aur.bats @@ -314,22 +314,30 @@ upgrade_steps() { run skup --no-refresh <<<$'1??\nq' assert_success log_has "$AS_ALICE paru -Sia pfetch" - assert_line '# lines starting with > come from the upstream candidate: a differing value, or a field the installed record lacks' - assert_output --partial "$(printf '%s\n' 'Version : 1.10.0-1' '> Version : 1.11.0-1')" - assert_line 'Install Reason : Explicitly installed' - assert_line 'Description : A pretty system information tool written in POSIX sh.' - [[ $(count_lines 'Description : A pretty system information tool written in POSIX sh.') -eq 1 ]] + assert_line '# lines starting with * come from the upstream candidate: a differing value, or a field the installed record lacks' + assert_output --partial "$(printf '%s\n' ' Version : 1.10.0-1' '* Version : 1.11.0-1' '* Repository : aur')" + assert_line ' Install Reason : Explicitly installed' + assert_line ' Description : A pretty system information tool written in POSIX sh.' + [[ $(grep -c 'Description : A pretty system' <<<"$output") -eq 1 ]] + # the AUR-only fields next to their installed counterparts, the rest at the end assert_output --partial "$(printf '%s\n' \ - '> Repository : aur' \ - '> AUR URL : https://aur.archlinux.org/packages/pfetch' \ - '> Make Deps : None' \ - '> Check Deps : None' \ - '> Maintainer : yigits' \ - '> Votes : 48' \ - '> Popularity : 0.10344' \ - '> First Submitted : Sat, 16 Jan 2021 09:13:08' \ - '> Last Modified : Tue, 9 Jun 2026 22:43:50' \ - '> Out Of Date : No')" + ' URL : https://github.com/Un1q32/pfetch' \ + '* AUR URL : https://aur.archlinux.org/packages/pfetch')" + assert_output --partial "$(printf '%s\n' \ + ' Optional Deps : None' \ + '* Make Deps : None' \ + '* Check Deps : None')" + assert_output --partial "$(printf '%s\n' \ + ' Packager : Unknown Packager' \ + '* Maintainer : yigits' \ + ' Build Date : Sat Oct 10 12:42:02 2026' \ + '* Last Modified : Tue, 9 Jun 2026 22:43:50' \ + '* First Submitted : Sat, 16 Jan 2021 09:13:08')" + assert_output --partial "$(printf '%s\n' \ + ' Validated By : None' \ + '* Votes : 48' \ + '* Popularity : 0.10344' \ + '* Out Of Date : No')" } @test "N?? with yay: its wider key column is re-padded to pacman's" { @@ -337,45 +345,149 @@ upgrade_steps() { run skup --no-refresh <<<$'1??\nq' assert_success log_has "$AS_ALICE yay -Sia pfetch" - assert_output --partial "$(printf '%s\n' 'Version : 1.10.0-1' '> Version : 1.11.0-1')" - assert_line '> Repository : aur' - assert_line '> Keywords : fetch' - assert_line '> First Submitted : Sat 16 Jan 2021 09:13:08 AM UTC' - assert_line '> Out-of-date : No' + assert_output --partial "$(printf '%s\n' ' Version : 1.10.0-1' '* Version : 1.11.0-1' '* Repository : aur')" + assert_output --partial "$(printf '%s\n' ' Groups : None' '* Keywords : fetch')" + assert_line '* First Submitted : Sat 16 Jan 2021 09:13:08 AM UTC' + assert_output --partial "$(printf '%s\n' ' Validated By : None' '* Popularity : 0.103440' '* Votes : 48' '* Out-of-date : No')" refute_output --regexp 'Repository {10,}:' } @test "N?? when the AUR has no record of it: the installed record alone" { run skup --no-refresh <<<$'3??\nq' assert_success - assert_line 'Name : su-dummy-aur' - assert_line 'Install Reason : Explicitly installed' - refute_line --regexp '^> [A-Z#]' + assert_line ' Name : su-dummy-aur' + assert_line ' Install Reason : Explicitly installed' + refute_line --regexp '^\* ' log_has "$AS_ALICE paru -Sia su-dummy-aur" } -@test "N?? when the AUR record does not merge: -Qi, then the AUR record with every line marked >" { +@test "N?? claude-desktop (recorded): yay's wrapped record unwrapped, one-line lists, Optional Deps per entry, AUR fields in place" { + # real records: the installed 2.9939.4-1 (pacman -Qi of a stand-in built from the AUR's own + # .SRCINFO of that version) and yay 13.0.1's -Sia of 2.31226.1-1, wrapped at 80 columns as yay + # prints it without COLUMNS=0 (discovery/pacman.md section 15.2) + use_fakes yay pacman runuser getent + run skup show claude-desktop + 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 : claude-desktop + Version : 2.9939.4-1 +* Version : 2.31226.1-1 +* Repository : aur + Description : Official Claude AI desktop app from Anthropic — Chat, Cowork, and Claude Code + Architecture : x86_64 + URL : https://claude.com/download +* AUR URL : https://aur.archlinux.org/packages/claude-desktop + Licenses : LicenseRef-Proprietary + Groups : None +* Keywords : None + Provides : None + Depends On : alsa-lib at-spi2-core cairo dbus expat gcc-libs glib2 glibc gtk3 hicolor-icon-theme libcap-ng libcups libdrm libnotify libseccomp libsecret libx11 libxcb libxcomposite libxdamage libxext libxfixes libxkbcommon libxrandr libxtst mesa nspr nss pango socat systemd-libs util-linux-libs virtiofsd xdg-desktop-portal xdg-utils +* Depends On : alsa-lib at-spi2-core bubblewrap cairo dbus expat gcc-libs glib2 glibc gtk3 hicolor-icon-theme libcap-ng libcups libdrm libnotify libpipewire libseccomp libsecret libx11 libxcb libxcomposite libxdamage libxext libxfixes libxkbcommon libxrandr libxtst mesa nspr nss pango socat systemd-libs util-linux-libs virtiofsd xdg-desktop-portal xdg-utils qemu-system-x86 edk2-ovmf + Optional Deps : gnome-keyring: credential storage via Secret Service (GNOME) + kwallet: credential storage (KDE Plasma) [installed] + libayatana-appindicator: system tray icon + xdg-desktop-portal-gtk: portal backend for GTK desktops + xdg-desktop-portal-kde: portal backend for KDE Plasma +* Optional Deps : gnome-keyring +* kwallet +* libayatana-appindicator +* xdg-desktop-portal-gtk +* xdg-desktop-portal-kde +* qemu-system-aarch64 +* edk2-aarch64 +* Make Deps : None +* Check Deps : None + Required By : None + Optional For : None + Conflicts With : claude claude-desktop-bin + Replaces : None + Installed Size : 117.74 MiB + Packager : Unknown Packager +* Maintainer : diazkev314 + Build Date : Sat Oct 3 04:00:00 2026 +* Last Modified : Fri 09 Oct 2026 11:31:19 PM UTC +* First Submitted : Sun 05 Jul 2026 01:58:02 AM UTC + Install Date : Sat Oct 10 13:49:52 2026 + Install Reason : Explicitly installed + Install Script : No + Validated By : None +* Popularity : 6.458490 +* Votes : 21 +* Out-of-date : No +EOF +)" + log_has "$AS_ALICE yay -Sia claude-desktop" +} + +@test "N?? claude-desktop: paru's unwrapped record merges to the same Depends On and Optional Deps lines" { + use_fakes yay pacman runuser getent + run skup show claude-desktop + yay_out=$output + use_fakes paru pacman runuser getent + run skup show claude-desktop + assert_success + [[ $(grep -A8 '^ Depends On' <<<"$output") == "$(grep -A8 '^ Depends On' <<<"$yay_out")" ]] || + fail "paru and yay merge Depends On / Optional Deps differently" + assert_line '* Out Of Date : No' +} + +@test "N?? pacman lists: a wrapped, reordered Depends On and bare Optional Deps names equal to the installed sets print once" { + use_fakes yay pacman runuser getent + FAKE_AUR_SIA=same run skup show claude-desktop + assert_success + [[ $(count_lines ' Depends On : alsa-lib at-spi2-core cairo dbus expat gcc-libs glib2 glibc gtk3 hicolor-icon-theme libcap-ng libcups libdrm libnotify libseccomp libsecret libx11 libxcb libxcomposite libxdamage libxext libxfixes libxkbcommon libxrandr libxtst mesa nspr nss pango socat systemd-libs util-linux-libs virtiofsd xdg-desktop-portal xdg-utils') -eq 1 ]] + refute_line --regexp '^\* +(Depends On|Optional Deps|Conflicts With) *:' + refute_line --regexp '^\* {18,}' + assert_output --partial "$(printf '%s\n' \ + ' kwallet: credential storage (KDE Plasma) [installed]' \ + ' libayatana-appindicator: system tray icon' \ + ' xdg-desktop-portal-gtk: portal backend for GTK desktops' \ + ' xdg-desktop-portal-kde: portal backend for KDE Plasma' \ + '* Make Deps : None')" +} + +@test "N?? pacman lists: an added token or a changed version constraint is a difference (one * line, as -Qi prints it)" { + use_fakes yay pacman runuser getent + FAKE_AUR_SIA=constraint run skup show claude-desktop + assert_success + assert_line --regexp '^\* Depends On : xdg-utils xdg-desktop-portal virtiofsd .* pango nss>=3\.100 nspr .* at-spi2-core alsa-lib$' + [[ $(grep -c '^\* Depends On' <<<"$output") -eq 1 ]] + refute_line --regexp '^\* +(Optional Deps|Conflicts With) *:' + run skup show claude-desktop + assert_line --regexp '^\* Depends On : alsa-lib at-spi2-core bubblewrap cairo .* libnotify libpipewire libseccomp .* xdg-utils qemu-system-x86 edk2-ovmf$' + # an extra upstream name in Optional Deps: the upstream block, one entry per line + assert_output --partial "$(printf '%s\n' \ + '* Optional Deps : gnome-keyring' \ + '* kwallet' \ + '* libayatana-appindicator' \ + '* xdg-desktop-portal-gtk' \ + '* xdg-desktop-portal-kde' \ + '* qemu-system-aarch64' \ + '* edk2-aarch64')" +} + +@test "N?? when the AUR record does not merge: -Qi, then the AUR record with every line marked *" { FAKE_AUR_SIA=twice run skup --no-refresh <<<$'1??\nq' assert_success - assert_line '# lines starting with > are the AUR record (paru -Sia) as it prints it' - refute_line --partial '# lines starting with > come from the upstream candidate' - assert_line 'Version : 1.10.0-1' - [[ $(count_lines '> Version : 1.11.0-1') -eq 2 ]] - [[ $(count_lines '> Repository : aur') -eq 2 ]] - assert_line 'Install Reason : Explicitly installed' + assert_line '# lines starting with * are the AUR record (paru -Sia) as it prints it' + refute_line --partial '# lines starting with * come from the upstream candidate' + assert_line ' Version : 1.10.0-1' + [[ $(count_lines '* Version : 1.11.0-1') -eq 2 ]] + [[ $(count_lines '* Repository : aur') -eq 2 ]] + assert_line ' Install Reason : Explicitly installed' } @test "show PKG for an AUR package: the same merge, the helper decided on the spot" { run skup show pfetch assert_success - assert_line '> Repository : aur' - assert_output --partial "$(printf '%s\n' 'Version : 1.10.0-1' '> Version : 1.11.0-1')" + assert_output --partial "$(printf '%s\n' ' Version : 1.10.0-1' '* Version : 1.11.0-1' '* Repository : aur')" export SKEPTICAL_UPDATER_EUID=1000 unset SUDO_USER : >"$FAKE_LOG" run skup show pfetch assert_success - assert_line '> Maintainer : yigits' + assert_line '* Maintainer : yigits' log_has 'paru -Sia pfetch' log_lacks '^runuser' } @@ -384,8 +496,8 @@ upgrade_steps() { unset SUDO_USER run skup show pfetch assert_success - assert_line 'Name : pfetch' - refute_line --regexp '^> ' + assert_line ' Name : pfetch' + refute_line --regexp '^\* ' [[ $(grep -c 'warning: aur:' <<<"$output") -eq 1 ]] log_lacks '^paru' } diff --git a/tests/unit/data/aur/Sia-paru-claude-desktop.txt b/tests/unit/data/aur/Sia-paru-claude-desktop.txt new file mode 100644 index 0000000..99df1a6 --- /dev/null +++ b/tests/unit/data/aur/Sia-paru-claude-desktop.txt @@ -0,0 +1,21 @@ +Repository : aur +Name : claude-desktop +Version : 2.31226.1-1 +Description : Official Claude AI desktop app from Anthropic — Chat, Cowork, and Claude Code +URL : https://claude.com/download +AUR URL : https://aur.archlinux.org/packages/claude-desktop +Groups : None +Licenses : LicenseRef-Proprietary +Provides : None +Depends On : alsa-lib at-spi2-core bubblewrap cairo dbus expat gcc-libs glib2 glibc gtk3 hicolor-icon-theme libcap-ng libcups libdrm libnotify libpipewire libseccomp libsecret libx11 libxcb libxcomposite libxdamage libxext libxfixes libxkbcommon libxrandr libxtst mesa nspr nss pango socat systemd-libs util-linux-libs virtiofsd xdg-desktop-portal xdg-utils qemu-system-x86 edk2-ovmf +Make Deps : None +Check Deps : None +Optional Deps : gnome-keyring kwallet libayatana-appindicator xdg-desktop-portal-gtk xdg-desktop-portal-kde qemu-system-aarch64 edk2-aarch64 +Conflicts With : claude claude-desktop-bin +Maintainer : diazkev314 +Votes : 21 +Popularity : 6.45849 +First Submitted : Sun, 5 Jul 2026 01:58:02 +Last Modified : Fri, 9 Oct 2026 23:31:19 +Out Of Date : No + diff --git a/tests/unit/data/aur/Sia-yay-claude-desktop-constraint.txt b/tests/unit/data/aur/Sia-yay-claude-desktop-constraint.txt new file mode 100644 index 0000000..73bb61a --- /dev/null +++ b/tests/unit/data/aur/Sia-yay-claude-desktop-constraint.txt @@ -0,0 +1,31 @@ +Repository : aur +Name : claude-desktop +Version : 2.31226.1-1 +Description : Official Claude AI desktop app from Anthropic — Chat, Cowork, and Claude Code +URL : https://claude.com/download +Licenses : LicenseRef-Proprietary +Groups : None +Provides : None +Depends On : xdg-utils xdg-desktop-portal virtiofsd + util-linux-libs systemd-libs socat pango nss>=3.100 + nspr mesa libxtst libxrandr libxkbcommon + libxfixes libxext libxdamage libxcomposite + libxcb libx11 libsecret libseccomp libnotify + libdrm libcups libcap-ng hicolor-icon-theme gtk3 + glibc glib2 gcc-libs expat dbus cairo + at-spi2-core alsa-lib +Optional Deps : xdg-desktop-portal-kde xdg-desktop-portal-gtk + libayatana-appindicator kwallet gnome-keyring +Make Deps : None +Check Deps : None +Conflicts With : claude claude-desktop-bin +Replaces : None +AUR URL : https://aur.archlinux.org/packages/claude-desktop +First Submitted : Sun 05 Jul 2026 01:58:02 AM UTC +Keywords : None +Last Modified : Fri 09 Oct 2026 11:31:19 PM UTC +Maintainer : diazkev314 +Popularity : 6.458490 +Votes : 21 +Out-of-date : No + diff --git a/tests/unit/data/aur/Sia-yay-claude-desktop-same.txt b/tests/unit/data/aur/Sia-yay-claude-desktop-same.txt new file mode 100644 index 0000000..c006dd4 --- /dev/null +++ b/tests/unit/data/aur/Sia-yay-claude-desktop-same.txt @@ -0,0 +1,31 @@ +Repository : aur +Name : claude-desktop +Version : 2.31226.1-1 +Description : Official Claude AI desktop app from Anthropic — Chat, Cowork, and Claude Code +URL : https://claude.com/download +Licenses : LicenseRef-Proprietary +Groups : None +Provides : None +Depends On : xdg-utils xdg-desktop-portal virtiofsd + util-linux-libs systemd-libs socat pango nss + nspr mesa libxtst libxrandr libxkbcommon + libxfixes libxext libxdamage libxcomposite + libxcb libx11 libsecret libseccomp libnotify + libdrm libcups libcap-ng hicolor-icon-theme gtk3 + glibc glib2 gcc-libs expat dbus cairo + at-spi2-core alsa-lib +Optional Deps : xdg-desktop-portal-kde xdg-desktop-portal-gtk + libayatana-appindicator kwallet gnome-keyring +Make Deps : None +Check Deps : None +Conflicts With : claude claude-desktop-bin +Replaces : None +AUR URL : https://aur.archlinux.org/packages/claude-desktop +First Submitted : Sun 05 Jul 2026 01:58:02 AM UTC +Keywords : None +Last Modified : Fri 09 Oct 2026 11:31:19 PM UTC +Maintainer : diazkev314 +Popularity : 6.458490 +Votes : 21 +Out-of-date : No + diff --git a/tests/unit/data/aur/Sia-yay-claude-desktop.txt b/tests/unit/data/aur/Sia-yay-claude-desktop.txt new file mode 100644 index 0000000..2c2e54e --- /dev/null +++ b/tests/unit/data/aur/Sia-yay-claude-desktop.txt @@ -0,0 +1,34 @@ +Repository : aur +Name : claude-desktop +Version : 2.31226.1-1 +Description : Official Claude AI desktop app from Anthropic — Chat, Cowork, and Claude Code +URL : https://claude.com/download +Licenses : LicenseRef-Proprietary +Groups : None +Provides : None +Depends On : alsa-lib at-spi2-core bubblewrap cairo dbus + expat gcc-libs glib2 glibc gtk3 + hicolor-icon-theme libcap-ng libcups libdrm + libnotify libpipewire libseccomp libsecret + libx11 libxcb libxcomposite libxdamage + libxext libxfixes libxkbcommon libxrandr + libxtst mesa nspr nss pango socat + systemd-libs util-linux-libs virtiofsd + xdg-desktop-portal xdg-utils qemu-system-x86 + edk2-ovmf +Optional Deps : gnome-keyring kwallet libayatana-appindicator + xdg-desktop-portal-gtk xdg-desktop-portal-kde + qemu-system-aarch64 edk2-aarch64 +Make Deps : None +Check Deps : None +Conflicts With : claude claude-desktop-bin +Replaces : None +AUR URL : https://aur.archlinux.org/packages/claude-desktop +First Submitted : Sun 05 Jul 2026 01:58:02 AM UTC +Keywords : None +Last Modified : Fri 09 Oct 2026 11:31:19 PM UTC +Maintainer : diazkev314 +Popularity : 6.458490 +Votes : 21 +Out-of-date : No + diff --git a/tests/unit/data/pacman/Qi.txt b/tests/unit/data/pacman/Qi.txt index a5a3460..6d81387 100644 --- a/tests/unit/data/pacman/Qi.txt +++ b/tests/unit/data/pacman/Qi.txt @@ -332,3 +332,29 @@ Install Date : Fri Oct 9 08:00:00 2026 Install Reason : Explicitly installed Install Script : No Validated By : None + +Name : claude-desktop +Version : 2.9939.4-1 +Description : Official Claude AI desktop app from Anthropic — Chat, Cowork, and Claude Code +Architecture : x86_64 +URL : https://claude.com/download +Licenses : LicenseRef-Proprietary +Groups : None +Provides : None +Depends On : alsa-lib at-spi2-core cairo dbus expat gcc-libs glib2 glibc gtk3 hicolor-icon-theme libcap-ng libcups libdrm libnotify libseccomp libsecret libx11 libxcb libxcomposite libxdamage libxext libxfixes libxkbcommon libxrandr libxtst mesa nspr nss pango socat systemd-libs util-linux-libs virtiofsd xdg-desktop-portal xdg-utils +Optional Deps : gnome-keyring: credential storage via Secret Service (GNOME) + kwallet: credential storage (KDE Plasma) [installed] + libayatana-appindicator: system tray icon + xdg-desktop-portal-gtk: portal backend for GTK desktops + xdg-desktop-portal-kde: portal backend for KDE Plasma +Required By : None +Optional For : None +Conflicts With : claude claude-desktop-bin +Replaces : None +Installed Size : 117.74 MiB +Packager : Unknown Packager +Build Date : Sat Oct 3 04:00:00 2026 +Install Date : Sat Oct 10 13:49:52 2026 +Install Reason : Explicitly installed +Install Script : No +Validated By : None diff --git a/tests/unit/fakes/paru/paru b/tests/unit/fakes/paru/paru index 44bf12e..3e07260 100755 --- a/tests/unit/fakes/paru/paru +++ b/tests/unit/fakes/paru/paru @@ -11,8 +11,9 @@ # noise (yay's colored root warning on stdout), none (nothing to upgrade: exit 1, no output) or # fail (the AUR is unreachable: exit 1 with the recorded error). Packages purged by the fake pacman # are not listed. -Sia PKG prints data/aur/Sia--PKG.txt (else the helper's not-found -# error, exit 1), twice with FAKE_AUR_SIA=twice (a record that cannot be merged); -Sua exits -# FAKE_AUR_SUA_RC (default 0). +# error, exit 1), twice with FAKE_AUR_SIA=twice (a record that cannot be merged), or the variant +# Sia--PKG-.txt when it exists; -Sua exits FAKE_AUR_SUA_RC (default 0). +# claude-desktop: yay's record is the wrapped one yay prints without COLUMNS=0, recorded verbatim. # shellcheck source=../fakelib.bash . "${BASH_SOURCE[0]%/*}/../fakelib.bash" fake_log "$@" @@ -38,6 +39,10 @@ case ${1:-} in printf '%s\n' "$out" ;; -Sia) + if [[ -n ${FAKE_AUR_SIA:-} && -f $D/Sia-$FAKE_NAME-${2:-}-$FAKE_AUR_SIA.txt ]]; then + cat "$D/Sia-$FAKE_NAME-$2-$FAKE_AUR_SIA.txt" + exit 0 + fi if [[ -f $D/Sia-$FAKE_NAME-${2:-}.txt ]]; then cat "$D/Sia-$FAKE_NAME-$2.txt" [[ ${FAKE_AUR_SIA:-} != twice ]] || cat "$D/Sia-$FAKE_NAME-$2.txt" diff --git a/tests/unit/interactive.bats b/tests/unit/interactive.bats index d614841..19e842c 100644 --- a/tests/unit/interactive.bats +++ b/tests/unit/interactive.bats @@ -93,11 +93,10 @@ UPGRADE='apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--forc @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 'Version: 1.0' - assert_line '> Version: 2.0' - assert_line '> Depends: su-dummy-b (>= 2.0)' + 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_output --partial "$(printf '%s\n' ' Version: 1.0' '* 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' } diff --git a/tests/unit/parsers.bats b/tests/unit/parsers.bats index dd1f0e0..c0d2ced 100644 --- a/tests/unit/parsers.bats +++ b/tests/unit/parsers.bats @@ -117,36 +117,35 @@ setup() { common_setup; } 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 -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 +# 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 + 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 'Depends: su-dummy-b' - assert_line '> Depends: su-dummy-b (>= 2.0)' + assert_output --partial "$(printf '%s\n' ' Depends: su-dummy-b' '* 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)" @@ -156,22 +155,22 @@ EOF # 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' + [[ $(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 '> ' + assert_line --index 0 '# not installed: lines starting with * come from the upstream candidate (all of them)' + assert_line --index 1 '* Package: zsh' + assert_line --index 3 '* Version: 5.9-4+b15' + refute_line --regexp '^ ' log_has 'dpkg-query -s -- zsh' } @@ -272,48 +271,49 @@ EOF assert_output 'trusted su-dummy-a (+1 dependency: su-dummy-b)' } -@test "pacman: show merges -Qi with -Si of the candidate's repository: '>' lines for what differs" { +@test "pacman: show merges -Qi with -Si of the candidate's repository: '*' lines for what differs, in place" { # 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 + # Optional Deps block (one entry per line), -Si-only fields next to their counterparts + # (Repository after Version, Download Size after Installed Size); every line in the gutter use_fakes pacman run skup show glib2 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 : 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) -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 +# 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 +* Repository : core + 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 +* Download Size : 5.12 MiB + Packager : Jan Alexander Steffens (heftig) + 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 EOF )" log_has 'pacman -Qi -- glib2' @@ -324,21 +324,21 @@ EOF 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' + 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 >' + 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 '> ' + assert_line --index 0 '# not installed: lines starting with * come from the upstream candidate (all of them)' + assert_line --index 1 '* Repository : su-dummy' + refute_line --regexp '^ ' run skup show nope assert_failure 1 } @@ -508,27 +508,27 @@ EOF 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). +# 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' @@ -536,9 +536,9 @@ EOF # 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 ]] + 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 ]] } @@ -547,31 +547,31 @@ EOF 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 : -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 : -> Vendor : -> Download size : 6.6 KiB -> Repository : su-dummy +# 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 : + 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 : +* Vendor : +* Download size : 6.6 KiB +* Repository : su-dummy EOF )" } @@ -580,16 +580,16 @@ EOF 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' + assert_line --index 0 '# not installed: lines starting with * come from the upstream candidate (all of them)' + assert_line --index 1 '* Name : zsh' + refute_line --partial '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' + refute_line --partial '# lines starting with *' + assert_line ' Installed Packages' + assert_line ' Available Upgrades' log_has 'dnf -q info su-dummy-a' } @@ -673,8 +673,8 @@ EOF use_fakes zypper rpm run skup show su-dummy-a assert_success - assert_line 'Information for package su-dummy-a:' - refute_line --partial '# lines starting with >' + 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 diff --git a/tests/unit/root.bats b/tests/unit/root.bats index 9bdd090..c1bc3fa 100644 --- a/tests/unit/root.bats +++ b/tests/unit/root.bats @@ -43,7 +43,7 @@ setup() { assert_line 'required by: su-dummy-a' run skup show su-dummy-a assert_success - assert_line 'Package: su-dummy-a' + assert_line ' Package: su-dummy-a' } @test "--dry-run works unprivileged: the refresh is printed, not run" {