feat(aur): list and upgrade AUR packages through paru or yay on Arch
On the pacman backend, an installed AUR helper (paru, else yay;
--aur-helper=paru|yay|none, --no-aur) adds the AUR updates to the review
list. The helper never runs as root: when the tool is root through sudo it
runs as SUDO_USER (runuser -u, else sudo -u, with that user's HOME, from
that home); unprivileged it runs as is; a root login gets no AUR, with a
warning. The banner names the helper ("+ AUR via paru").
- listing: <helper> -Qua (also with --no-refresh), rows with repository
"aur"; [ignored] is shown [held]; a failing -Qua warns and the session
goes on with the repository rows
- every row has a marker column after the number: "*" for AUR rows, a
space otherwise (on every backend)
- N?? merges pacman -Qi with <helper> -Sia; N?, N+, Nx stay with pacman;
N- excludes through the helper's --ignore
- <enter>: the repository step exactly as before, then <helper> -Sua
--ignore <excluded> as the invoking user (--noconfirm only when stdin is
not a terminal); pacman is skipped when only AUR rows are left; a helper
failure is reported and its status returned; --dry-run prints it
- other backends never run a helper; --aur-helper is ignored with a warning
paru 2.1.0 and yay 13.0.1 formats verified in discovery/pacman.md section
15 (proof: discovery/pacman/aur.sh). Unit tests replay the recorded output
(fake paru, yay, runuser, getent); the archlinux E2E run installs the
foreign su-dummy-aur and drives a fake paru through a fake sudo built on
passwordless su for wheel.
This commit is contained in:
1 parent
875c8294bf
commit
def537af58
35 files changed
+1828
-40
No files matched your search
@@ -6,7 +6,9 @@ upgradable packages, lets the user interrogate each one, and only then runs the
|
||||
native upgrade. Packages the user has explicitly acknowledged are remembered in a
|
||||
statefile and never questioned again. It is one file with no runtime dependencies
|
||||
beyond bash >= 4.3 and the distro's package manager, and it never reimplements
|
||||
package management: every action shells out to the native tool.
|
||||
package management: every action shells out to the native tool. On Arch Linux and
|
||||
its derivatives, AUR packages join the review when `paru` or `yay` is installed
|
||||
(see [AUR packages](#aur-packages-arch-linux-paru-or-yay)).
|
||||
|
||||
The specification is [docs/DESIGN.md](docs/DESIGN.md); the per-family command
|
||||
research it is built on lives in [discovery/](discovery/).
|
||||
@@ -27,7 +29,9 @@ on first use, not by `make install`; `make uninstall` leaves it in place.
|
||||
Runtime needs: bash >= 4.3, the package manager, coreutils, sed and grep (plus awk,
|
||||
which the apt and pacman backends use and which those distros always ship). `sudo`
|
||||
is optional: when the tool needs root and you are not root, it re-runs itself
|
||||
through `sudo` with the same arguments, or fails with a clear message.
|
||||
through `sudo` with the same arguments, or fails with a clear message. On the pacman
|
||||
backend `paru` or `yay` is optional too (AUR packages); the helper is run as the user
|
||||
who invoked `sudo`, with `runuser` (util-linux, always there on Arch) or else `sudo -u`.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -51,6 +55,9 @@ options:
|
||||
--dry-run print native commands instead of running mutating ones
|
||||
--state-dir=DIR override /var/lib/skeptical-updater (env: SKEPTICAL_UPDATER_STATE_DIR)
|
||||
--no-color disable ANSI colours (also honours NO_COLOR and non-tty stdout)
|
||||
--aur-helper=NAME pacman backend: list and upgrade AUR packages with paru or yay, or
|
||||
none (default: paru, else yay, when installed; other backends ignore it)
|
||||
--no-aur same as --aur-helper=none
|
||||
-h, --help this help
|
||||
-V, --version print the version
|
||||
```
|
||||
@@ -60,7 +67,8 @@ otherwise the exit status of the package manager's upgrade step.
|
||||
|
||||
What needs root: the metadata refresh, the upgrade, a purge, and writing a state
|
||||
directory you cannot write. `--dry-run`, `list --no-refresh`, `trusted`, `info` and
|
||||
`show` work unprivileged (as long as the state directory is readable).
|
||||
`show` work unprivileged (as long as the state directory is readable); there an AUR
|
||||
helper simply runs as you.
|
||||
|
||||
### Example session
|
||||
|
||||
@@ -115,7 +123,8 @@ upgrade; end of input without one aborts with exit status 2).
|
||||
`[held]` marks packages the package manager will not upgrade on its own: apt
|
||||
holds and "kept back" packages, pacman `IgnorePkg` (`[ignored]` in `pacman -Qu`).
|
||||
dnf `exclude=`/versionlock and zypper locks hide packages from the native listing,
|
||||
so they do not appear at all.
|
||||
so they do not appear at all. The column right after the number is a `*` for an AUR
|
||||
package (pacman backend with paru or yay, see below) and a space on every other row.
|
||||
|
||||
### Command grammar
|
||||
|
||||
@@ -188,6 +197,73 @@ replacements only the pacman transaction (`-Sup`) shows, packages dnf installs t
|
||||
others, packages a Tumbleweed `dist-upgrade` removes. With no row but such work, the list
|
||||
says "0 to review, but ... reports pending changes" and `<enter>` still runs the upgrade.
|
||||
|
||||
### AUR packages (Arch Linux: paru or yay)
|
||||
|
||||
On the pacman backend (Arch and its derivatives, `pman` included) AUR packages are reviewed
|
||||
with the rest when an AUR helper is installed: `paru`, else `yay`. `--aur-helper=paru|yay`
|
||||
picks one (an error if it is not installed), `--aur-helper=none` or `--no-aur` turns the AUR
|
||||
off. The banner says which helper is in use, the AUR rows get a `*` right after the number
|
||||
and `aur` as their repository:
|
||||
|
||||
```
|
||||
$ skeptical-updater (as a normal user: it re-runs itself through sudo)
|
||||
Backend: pacman (Arch Linux) + AUR via paru
|
||||
Refreshing package lists... done.
|
||||
4 upgradable, 0 trusted (hidden, use --all to show), 4 to review:
|
||||
|
||||
[1] su-dummy-a 1.0-1 -> 2.0-1 su-dummy
|
||||
[2]* su-dummy-aur 1.0-1 -> 2.0-1 aur
|
||||
[3] su-dummy-b 1.0-1 -> 2.0-1 su-dummy
|
||||
[4] su-dummy-c 1.0-1 -> 2.0-1 su-dummy
|
||||
|
||||
<enter> upgrade | N? what is it | N?? full info | N- exclude this run | Nx purge now | N+ trust forever
|
||||
> 3-
|
||||
excluded from this run: su-dummy-b
|
||||
>
|
||||
Excluded from this run: su-dummy-b
|
||||
Checking the reviewed transaction (pacman -Sup)... ok.
|
||||
+ cp -p -- /tmp/skeptical-updater-db.4242.AbC123/sync/su-dummy.db /var/lib/pacman/sync/
|
||||
+ pacman -Su --noconfirm --ignore su-dummy-b
|
||||
...
|
||||
+ runuser -u alice -- env HOME=/home/alice paru -Sua --ignore su-dummy-b
|
||||
...
|
||||
Upgrade finished.
|
||||
```
|
||||
|
||||
(`pman` installed: `Backend: pman (Arch Linux) + AUR via paru`.)
|
||||
|
||||
- **Listing**: `paru -Qua` / `yay -Qua` (`name old -> new`). The helper asks the AUR every time,
|
||||
so `--no-refresh` lists the AUR rows too; when it fails (no network, the AUR is down) its
|
||||
message is shown with `aur: paru -Qua failed, AUR packages not listed` and the session goes on
|
||||
with the repository rows. A package in `IgnorePkg` is listed `[held]` by paru and left out by
|
||||
yay. Devel (`-git`) packages are listed when the helper checks them (paru does by default, yay
|
||||
with `--devel`), with `latest-commit` as the new version. A name a repository has wins over an
|
||||
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).
|
||||
- **`N-`** excludes an AUR row through the helper's `--ignore`.
|
||||
- **`<enter>`** 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
|
||||
`paru -Sua --ignore <every excluded row>` (or yay). The helper runs only after a successful
|
||||
repository step; if it fails, its exit status is the tool's (`the AUR upgrade failed: paru
|
||||
exited with status 1 (the repository packages were upgraded)`). When no repository row is
|
||||
left to upgrade, pacman is not run at all, only the helper. `--dry-run` prints the helper's
|
||||
command line.
|
||||
- **Which user**: AUR helpers must not run as root (paru refuses to build as root, makepkg too).
|
||||
When the tool is root because of its own `sudo` re-exec (or any `sudo`: `SUDO_USER` names a
|
||||
non-root user), every helper command runs as that user: `runuser -u "$SUDO_USER" -- env
|
||||
HOME=<that user's home> paru ...` from that home (`sudo -u` when `runuser` is missing). Run
|
||||
unprivileged (`list --no-refresh`, `info`, `show`, `--dry-run`) the helper runs as you. A root
|
||||
login without `SUDO_USER` gets no AUR, with a warning (`aur: paru is never run as root and
|
||||
there is no invoking user ...`).
|
||||
- **PKGBUILD review**: when stdin is a terminal the helper gets it and asks as usual - paru's
|
||||
PKGBUILD review, yay's diff menu, and `sudo` asking for your password for its `pacman -U` if
|
||||
the sudo timestamp has expired meanwhile. When stdin is not a terminal (a piped session) the
|
||||
helper runs with `--noconfirm`: nothing is reviewed, and its `sudo pacman -U` must work without
|
||||
a password.
|
||||
|
||||
### Line editing and history
|
||||
|
||||
When stdin is a terminal, the prompt is read with bash's readline and its defaults: Left/Right
|
||||
@@ -263,12 +339,14 @@ Permanent holds belong to the package manager (`apt-mark hold`, `IgnorePkg`,
|
||||
|---------|---------|---------|-------------------|-------|
|
||||
| apt | `apt-get update -qq -o APT::Update::Error-Mode=any` | `apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold -o DPkg::Lock::Timeout=60 --with-new-pkgs upgrade` | temporary `apt-mark hold` of the excluded packages that are *not already held*, journalled in `holds.added` before holding; afterwards exactly those are released (also on failure, Ctrl-C, TERM, HUP, QUIT, a second signal during the restore, or a closed stdout; a journal left by `kill -9` is replayed at the start of the next root run). Holds you placed are never touched. `apt-mark` ignores `DPkg::Lock::Timeout`, so it is retried for up to 60 s while dpkg's lock is busy. | plan: `apt-get -s purge --auto-remove PKG` minus what `apt-get -s autoremove` already listed (`--auto-remove` is system-wide), then `apt-get -y purge` of exactly that set: PKG and the dependencies this purge orphans; no `autoremove` |
|
||||
| pacman / pman | `pacman -Sy --dbpath <throwaway copy>` (with `local` linked to the real local database, seeded with the system's `*.db` and `*.db.sig`): listing, `q`, end of input and `--dry-run` never touch `/var/lib/pacman/sync`; errors pacman prints about it are shown | check `pacman -Sup --noconfirm --ignore ... --dbpath <throwaway>` (on failure: nothing changed, its exit status), install the reviewed `*.db`/`*.db.sig` into `/var/lib/pacman/sync` under `db.lck`, all-or-nothing, never over a newer system database (exit 2), then `pacman -Su --noconfirm` - exactly the reviewed metadata, no second sync; with `--no-refresh` plain `pacman -Su` | `--ignore a,b` | plan: `pacman -Rs --print`; then `pacman -Rns --noconfirm -- ...` |
|
||||
| AUR (pacman backend, paru or yay) | none needed: `-Qua` asks the AUR every time | after the repository step: `paru -Sua` (`--noconfirm` when stdin is not a terminal), as the user who invoked sudo | the helper's `--ignore a,b` (all excluded rows) | through pacman, like any installed package |
|
||||
| dnf / yum | `dnf -q --refresh --setopt=skip_if_unavailable=0 makecache` | `dnf -y --setopt=skip_if_unavailable=0 --setopt=*.metadata_expire=-1 upgrade` (the reviewed metadata; the repo glob is needed because a repo's own `metadata_expire` overrides the main one) | `--exclude=a,b` (adds to `exclude=` from the configuration) | plan: `dnf --assumeno remove PKG`; then `dnf -y remove ...` |
|
||||
| zypper | `zypper --non-interactive --quiet refresh` | `zypper --non-interactive --no-refresh update` (Leap/SLE) or `dist-upgrade` (Tumbleweed/Slowroll, by `ID=` in os-release) | locks in a *private* `ZYPP_CONF`: the effective configuration (main zypp.conf plus the `zypp.conf.d` drop-ins of `/usr/etc` and `/etc`) with `locksfile.path` pointing at a copy of the effective lock file (also when a drop-in sets it), plus `addlock` of the exclusions; deleted afterwards: the real lock file is never written | plan: `zypper --xmlout remove --dry-run --clean-deps PKG`; then `zypper --non-interactive --no-refresh remove --clean-deps ...` |
|
||||
|
||||
Every package-manager call runs with `LC_ALL=C` (and `DEBIAN_FRONTEND=noninteractive`;
|
||||
`COLUMNS=0` for pacman), with stdin from `/dev/null` so that it can never eat the
|
||||
commands you pipe in. The upgradable list comes from `apt-get -s --with-new-pkgs
|
||||
commands you pipe in - except an AUR helper's upgrade on a terminal, which gets the terminal for
|
||||
its own prompts. The upgradable list comes from `apt-get -s --with-new-pkgs
|
||||
upgrade` (Inst lines + the "kept back" block), `pacman -Qu` joined with `pacman -Sl`,
|
||||
`dnf -q check-update` (+ `rpm -qa` for the installed versions) and
|
||||
`zypper --xmlout list-updates`, or on Tumbleweed (as root) the real plan
|
||||
@@ -303,8 +381,8 @@ runs a subset of distros; `E2E_FAIL_FAST=1` stops at the first failing distro.
|
||||
|
||||
No root, no network, no real package manager. The applet runs with a hermetic
|
||||
`PATH`: the fakes in `tests/unit/fakes/<backend>/` (`apt-get`, `apt-cache`,
|
||||
`dpkg-query`, `apt-mark`, `pacman`, `pman`, `dnf`, `yum`, `rpm`, `zypper`, `sudo`)
|
||||
in front of symlinks to the few system tools it needs. The fakes replay the real
|
||||
`dpkg-query`, `apt-mark`, `pacman`, `pman`, `dnf`, `yum`, `rpm`, `zypper`, `sudo`, and for the
|
||||
AUR `paru`, `yay`, `runuser`, `getent`) in front of symlinks to the few system tools it needs. The fakes replay the real
|
||||
outputs recorded in `discovery/*.md` (data in `tests/unit/data/`), keep their state
|
||||
(holds, purged packages) under the test's temp dir and log every invocation, so the
|
||||
tests assert the exact commands issued. Covered: CLI parsing and exit codes, backend
|
||||
@@ -320,9 +398,14 @@ 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), 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.
|
||||
short candidate descriptions, dnf5's padding, the fallbacks), 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,
|
||||
a root login), the `*` column on pacman and apt rows, `-Qua` parsing (`[ignored]`, yay's age suffix
|
||||
and root warning, devel packages, failures), `N?`/`N??`/`N-`/`N+`/`Nx` on AUR rows, the upgrade order
|
||||
and arguments, an AUR-only upgrade, a failing helper, `--dry-run`, and `--noconfirm` only without a
|
||||
terminal. About 35 seconds.
|
||||
|
||||
### End-to-end tests (`tests/e2e/`)
|
||||
|
||||
@@ -354,6 +437,20 @@ review is not installed), apt
|
||||
holds waiting for a busy dpkg lock, zypper honouring a lock file set in a drop-in and applying
|
||||
the reviewed metadata although a newer package appeared after the review.
|
||||
|
||||
`tests/e2e/aur.bats` covers the AUR. On archlinux the fixture (`FIXTURE_AUR=1`) also installs
|
||||
`su-dummy-aur` 1.0 with `pacman -U` (a foreign package: in no repository) and keeps its 2.0
|
||||
package file, creates the user `su-tester` and makes `su` passwordless for the `wheel` group;
|
||||
the tests install `fixtures/fake-paru.sh` as `/usr/local/bin/paru` (it emulates paru's `-Qua`,
|
||||
`-Sia` and `-Sua` for that package, offline, refusing to install as root like paru) and
|
||||
`fixtures/fake-sudo.sh` as `/usr/local/bin/sudo` (the image has no sudo: `su`, with `SUDO_USER`
|
||||
set like sudo does). The applet runs as `su-tester` and re-executes itself through that sudo, or
|
||||
as root with `SUDO_USER=su-tester`: the `*` row with repository `aur`, paru running as su-tester
|
||||
with its HOME, `N?` and `N??` on it, `N-` keeping it at 1.0 while the repository packages upgrade,
|
||||
a plain `<enter>` upgrading it to 2.0 after them, an AUR-only upgrade that leaves pacman alone,
|
||||
`--dry-run`, an unprivileged `list --no-refresh`, a root login (warning, no AUR) and `--no-aur`.
|
||||
On the other distros it checks the space column and that no AUR helper ever runs, even with
|
||||
`--aur-helper=paru` (ignored with a warning).
|
||||
|
||||
The matrix (`tests/docker/images.txt`):
|
||||
|
||||
| label | image | backend |
|
||||
@@ -442,6 +539,26 @@ From the discovery reports (`discovery/*.md`), all reproduced there:
|
||||
the current closures of all your trusted packages wholesale. A package shared by
|
||||
several trusted roots is recorded under the first one only, so `untrust` of that
|
||||
root drops it until the next `refresh`.
|
||||
- **AUR** (pacman backend with paru or yay):
|
||||
- The helper is run as the user named by `SUDO_USER` only: a root login (no `SUDO_USER`, or
|
||||
`doas`, `run0`, `pkexec`) lists and upgrades no AUR package, with a warning.
|
||||
- A piped session upgrades AUR packages with `--noconfirm`: no PKGBUILD review, and the
|
||||
helper's own `sudo pacman -U` must not ask for a password (NOPASSWD, or a sudo timestamp that
|
||||
is still valid). Review AUR updates on a terminal, or exclude their rows. On a terminal the
|
||||
helper may ask for your password again if its sudo timestamp expired during a long review.
|
||||
- The AUR listing needs the network on every run (also with `--no-refresh`); without it the
|
||||
session goes on with the repository rows (warning). `N??` on an AUR row asks the AUR too.
|
||||
- The helper decides what is foreign from the system's sync database, not from the reviewed
|
||||
throwaway copy; a package that moved from the AUR into a repository is listed from the
|
||||
repository (its AUR row is dropped).
|
||||
- What the helper reports is what you get: yay leaves `IgnorePkg` packages out (paru lists them
|
||||
`[held]`), devel packages are listed only when the helper checks them (paru by default, yay
|
||||
with `--devel`) and show `latest-commit` as the new version; the helper's own configuration
|
||||
(`paru.conf`, `yay --save`) applies to its upgrade.
|
||||
- The AUR upgrade is one helper run after `pacman -Su`: a helper failure leaves the repository
|
||||
packages upgraded (reported, its exit status returned). The E2E suite drives a fake paru
|
||||
(offline); the formats of real paru 2.1.0 and yay 13.0.1 are verified in
|
||||
`discovery/pacman.md` section 15.
|
||||
- The upgradable lists show what the native listing shows: new dependencies are rows on apt
|
||||
and on Tumbleweed (`-` as the old version); on pacman and dnf new dependencies,
|
||||
replacements and obsoletes only appear as a note, and removals only on Tumbleweed (note);
|
||||
|
||||
@@ -9,6 +9,7 @@ Companion files (all in `discovery/pacman/`):
|
||||
| `fixture.sh` | E2E fixture: dummy packages `su-dummy-{a,b,c}` 1.0 installed, 2.0 offered by a local repo |
|
||||
| `lib.sh` | proven bash helpers (`pm_upgradable`, `pm_field`, `pm_closure`, `pm_plan`, ...) |
|
||||
| `checks.sh` | executable proof of this document (`ALL CHECKS PASSED` on `archlinux:latest` and `archlinux:base-devel`) |
|
||||
| `aur.sh` | proof of section 15 (paru and yay output formats; **needs network**: Arch mirrors, the AUR, GitHub) |
|
||||
|
||||
Re-run the proof:
|
||||
|
||||
@@ -30,6 +31,7 @@ docker run --rm -i -v "$PWD/discovery/pacman:/d:ro" archlinux:latest bash /d/che
|
||||
| per-run exclusion | `pacman -Su --noconfirm --ignore a,b` (or repeated `--ignore`); globs work |
|
||||
| upgrade | `pacman -Su --noconfirm` (`-Syu` if you did not refresh before) |
|
||||
| purge | `pacman -Rns --noconfirm -- pkg...` |
|
||||
| AUR (paru / yay, section 15) | list `<helper> -Qua`, record `<helper> -Sia PKG`, upgrade `<helper> -Sua [--noconfirm] [--ignore a,b]`, never as root |
|
||||
|
||||
Top gotchas (details below):
|
||||
|
||||
@@ -615,3 +617,127 @@ Validated By : None
|
||||
* 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).
|
||||
|
||||
## 15. AUR helpers: paru and yay (`-Qua`, `-Sia`, `-Sua`)
|
||||
|
||||
Verified 2026-10-10 on archlinux:latest (pacman 7.1.0 / libalpm 16.0.1) with **paru 2.1.0** and **yay 13.0.1**,
|
||||
both built from the AUR, run as the unprivileged user `bob` (wheel, NOPASSWD sudo) with stdin from `/dev/null`,
|
||||
stdout piped, `LC_ALL=C`. `paru-bin` 2.1.0 does not start on this pacman (`paru: error while loading shared
|
||||
libraries: libalpm.so.15: cannot open shared object file`, exit 127: it links libalpm 15), so paru was built from
|
||||
the `paru` package (cargo); yay is `yay-bin`. Outdated AUR package: `pfetch` 1.10.0-1 (built from an older commit
|
||||
of its AUR git), 1.11.0-1 in the AUR; devel package: `pfetch-git`; foreign package that is not in the AUR:
|
||||
`su-local-only` (hand-built). Proof: `discovery/pacman/aur.sh` (needs network):
|
||||
|
||||
```
|
||||
docker run --rm -i -v "$PWD/discovery/pacman:/d:ro" archlinux:latest bash /d/aur.sh
|
||||
```
|
||||
|
||||
### 15.1 `-Qua`: the AUR upgrades
|
||||
|
||||
```
|
||||
$ paru -Qua $ yay -Qua
|
||||
pfetch 1.10.0-1 -> 1.11.0-1 pfetch 1.10.0-1 -> 1.11.0-1 [122d13h]
|
||||
[exit 0] [exit 0]
|
||||
```
|
||||
|
||||
* Format `name SP old SP "->" SP new [SP extra]`, like `pacman -Qu`; no colour when piped (both). yay 13 appends the
|
||||
time since the package's `Last Modified` in the AUR (`[122d13h]`: 9 Jun 2026 to 10 Oct 2026); paru nothing.
|
||||
`paru -Qu` / `yay -Qu` print the same line here (repository and AUR upgrades together).
|
||||
* `IgnorePkg = pfetch`: paru prints `pfetch 1.10.0-1 -> 1.11.0-1 [ignored]` (exit 0); **yay leaves the package
|
||||
out** (no output, exit 1).
|
||||
* Nothing to upgrade: **exit 1, nothing on stdout or stderr** (both, with and without `--devel`).
|
||||
* The AUR unreachable (`127.0.0.1 aur.archlinux.org` in `/etc/hosts`): **exit 1 with the error on stderr**:
|
||||
|
||||
```
|
||||
paru: error: error sending request for url (https://aur.archlinux.org/rpc): error trying to connect: tcp connect
|
||||
error: Connection refused (os error 111): ... Connection refused (os error 111) (one line)
|
||||
yay: -> 1 error occurred:
|
||||
* request failed: Get "https://aur.archlinux.org/rpc?arg%5B%5D=paru&...&type=info&v=5": dial tcp
|
||||
127.0.0.1:443: connect: connection refused (then 2 blank lines)
|
||||
```
|
||||
|
||||
So exit 1 with an empty stderr = nothing to upgrade; anything else = failure.
|
||||
* A foreign package that is not in the AUR is not mentioned by `-Qua` (only `-Sua` says `:: packages not in the
|
||||
AUR: su-local-only` / ` -> Packages not in AUR: su-local-only`). Foreign = in no sync database: with the
|
||||
repositories of the E2E fixture commented out, every base package is foreign to the helper.
|
||||
* Devel (VCS) packages, after `paru -S pfetch-git`, `paru --gendb`, `yay -Y --gendb` and replacing the commit ids
|
||||
recorded in `~/.local/state/paru/devel.toml` / `~/.cache/yay/vcs.json` by older ones: paru checks them **by
|
||||
default** (`paru -Qua`, with or without `--devel`; `-Sua` prints `:: Looking for devel upgrades...`), yay only
|
||||
with `--devel` (or `yay -Y --devel --save`). The new version is the literal `latest-commit`:
|
||||
|
||||
```
|
||||
$ paru -Qua $ yay -Qua --devel ($ yay -Qua: exit 1, nothing)
|
||||
pfetch-git r432.a906ff8-1 -> latest-commit pfetch-git r432.a906ff8-1 -> latest-commit [2299d6h]
|
||||
```
|
||||
|
||||
* As root: `paru -Qua` works (same output); `yay -Qua` works but prints `^[[1m^[[33m -> ^[[0m^[[0mAvoid running
|
||||
yay as root/sudo.` on **stdout**. `paru -Sua` as root: `error: can't install AUR package as root` (exit 1);
|
||||
`yay -Sua` as root fails as well (exit 1). The helpers must run as a normal user.
|
||||
|
||||
### 15.2 `-Sia PKG`: the AUR record
|
||||
|
||||
`paru -Si pfetch`, `paru -Sia pfetch` and `paru -Si --aur pfetch` print the same record; `yay -Si pfetch` asks the
|
||||
repositories only (`error: package 'pfetch' was not found` / ` -> exit status 1`, exit 1), `yay -Sia pfetch` gives
|
||||
the AUR record. Both are pacman-style, one record followed by a blank line:
|
||||
|
||||
```
|
||||
$ paru -Sia pfetch $ yay -Sia pfetch (keys padded to 30)
|
||||
Repository : aur Repository : aur
|
||||
Name : pfetch Name / Version / Description / URL / Licenses /
|
||||
Version : 1.11.0-1 Groups / Provides / Depends On / Optional Deps /
|
||||
Description : A pretty system information tool ... Make Deps / Check Deps / Conflicts With /
|
||||
URL : https://github.com/Un1q32/pfetch Replaces : None
|
||||
AUR URL : https://aur.archlinux.org/packages/pfetch AUR URL : https://aur...
|
||||
Groups : None First Submitted : Sat 16 Jan 2021 09:13:08 AM UTC
|
||||
Licenses : MIT Keywords : fetch
|
||||
Provides : pfetch Last Modified : Tue 09 Jun 2026 10:43:50 PM UTC
|
||||
Depends On : None Maintainer : yigits
|
||||
Make Deps : None Popularity : 0.103440
|
||||
Check Deps : None Votes : 48
|
||||
Optional Deps : None Out-of-date : No
|
||||
Conflicts With : pfetch-git
|
||||
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
|
||||
```
|
||||
|
||||
* Comparable with `pacman -Qi pfetch` (section 14): one record per side, every `:` in one column per side (paru: 17,
|
||||
like pacman; yay: 31, re-padded by the merge), keys spelled like pacman's. Both, here identical: `Name`,
|
||||
`Description`, `URL`, `Licenses`, `Groups`, `Provides`, `Depends On`, `Optional Deps`, `Conflicts With` (yay also
|
||||
`Replaces`); different: `Version`. `-Qi` only: `Architecture`, `Required By`, `Optional For`, `Installed Size`,
|
||||
`Packager`, `Build Date`, `Install Date`, `Install Reason`, `Install Script`, `Validated By`. AUR only:
|
||||
`Repository` (`aur`), `AUR URL`, `Make Deps`, `Check Deps`, `Maintainer`, `Votes`, `Popularity`, `First
|
||||
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.
|
||||
|
||||
### 15.3 `-Sua`: the AUR upgrade
|
||||
|
||||
* `--ignore` covers AUR packages (both): `paru -Sua --noconfirm --ignore pfetch` -> ` there is nothing to do`, stderr
|
||||
`warning: pfetch: ignoring package upgrade (1.10.0-1 => 1.11.0-1)`, exit 0; `yay -Sua --noconfirm --ignore pfetch`
|
||||
-> ` -> pfetch: ignoring package upgrade (1.10.0-1 => 1.11.0-1)` and ` there is nothing to do` (stdout), exit 0.
|
||||
Names that are not installed (`--ignore su-dummy-x,su-dummy-y`) are accepted and pfetch is upgraded.
|
||||
* `--noconfirm`, stdin `/dev/null`: both upgrade without asking (exit 0). paru still prints `:: Proceed to review?
|
||||
[Y/n]:` and answers it; yay's menus (`Packages to exclude`, `Packages to cleanBuild?`, `Diffs to show?`) take
|
||||
their default (none). The helper installs with `sudo pacman -U` itself (here NOPASSWD; otherwise sudo asks for
|
||||
the user's password on the terminal).
|
||||
* **Without `--noconfirm`, stdin `/dev/null`**: paru takes the defaults at end of input and upgrades (printing the
|
||||
PKGBUILD as its review); **yay exits 1** at its first menu (`:: 1 package to upgrade/install.` ... `Excluding
|
||||
packages may cause partial upgrades`), nothing upgraded. So `--noconfirm` whenever stdin is not a terminal; on a
|
||||
terminal the helper keeps its prompts (paru's PKGBUILD review, yay's diff menu).
|
||||
* Devel: `paru -Sua --noconfirm` rebuilt `pfetch-git` for the `latest-commit` row (`warning: pfetch-git-r432.a906ff8-1
|
||||
is up to date -- reinstalling`, since only the recorded commit was faked).
|
||||
* `runuser -u bob -- env` (as root) sets `HOME=/home/bob`, `USER`, `LOGNAME` and `MAIL` (PAM) and keeps `PATH` and
|
||||
the working directory; `runuser` is in util-linux (the base image has it, `sudo` it has not).
|
||||
|
||||
### 15.4 Chosen
|
||||
|
||||
| need | command (as the invoking user: `runuser -u "$SUDO_USER" -- env HOME=<home>`, else `sudo -u ...`; unprivileged runs as they are; a root login without `SUDO_USER`: no AUR) |
|
||||
|------|---------|
|
||||
| helper | `--aur-helper=paru\|yay\|none` / `--no-aur`, else `paru`, else `yay` |
|
||||
| list | `<helper> -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 `<helper> -Sia PKG` (section 14's merge) |
|
||||
| upgrade | after the repository step: `<helper> -Sua [--noconfirm when stdin is not a terminal] [--ignore <excluded rows>]` |
|
||||
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# discovery/pacman/aur.sh - verify the output formats and exit codes of paru and yay that the
|
||||
# AUR support of skeptical-updater relies on (-Qua, -Si/-Sia, -Sua --ignore, devel packages,
|
||||
# [ignored], root refusal, AUR unreachable). NEEDS NETWORK (Arch mirrors, aur.archlinux.org,
|
||||
# github.com for pfetch-git). Run as root in a throwaway container:
|
||||
# docker run --rm -i -v "$PWD/discovery/pacman:/d:ro" archlinux:latest bash /d/aur.sh
|
||||
set -uo pipefail
|
||||
export LC_ALL=C
|
||||
|
||||
U=bob
|
||||
H=/home/$U
|
||||
step() { printf '\n######## %s\n' "$*"; }
|
||||
# as_user CMD...: run CMD as the unprivileged user, from its home, with its HOME
|
||||
as_user() { (cd "$H" && runuser -u "$U" -- env HOME="$H" "$@"); }
|
||||
# show [-r] CMD...: print the command, its stdout, its stderr (prefixed "2> ") and its exit status;
|
||||
# as the user unless -r; escape sequences are made visible (cat -v)
|
||||
show() {
|
||||
local root=0 rc=0 o e
|
||||
[[ $1 == -r ]] && { root=1; shift; }
|
||||
o=$(mktemp) e=$(mktemp)
|
||||
printf '$ %s%s\n' "$( ((root)) && echo '(root) ')" "$*"
|
||||
if ((root)); then "$@" >"$o" 2>"$e" </dev/null || rc=$?; else as_user "$@" >"$o" 2>"$e" </dev/null || rc=$?; fi
|
||||
cat -v "$o"
|
||||
sed 's/^/2> /' "$e" | cat -v
|
||||
printf '[exit %s]\n' "$rc"
|
||||
rm -f "$o" "$e"
|
||||
}
|
||||
|
||||
step "setup: base-devel git sudo, user $U (wheel, NOPASSWD)"
|
||||
pacman -Syu --noconfirm --needed base-devel git sudo >/dev/null 2>&1 || { echo "pacman -Syu failed"; exit 1; }
|
||||
useradd -m -G wheel "$U"
|
||||
echo "$U ALL=(ALL:ALL) NOPASSWD: ALL" >/etc/sudoers.d/$U
|
||||
chmod 0440 /etc/sudoers.d/$U
|
||||
|
||||
# aur_build NAME [older]: clone NAME from the AUR and makepkg -si it; "older" checks out the newest
|
||||
# commit whose .SRCINFO pkgver differs from the current one first (an outdated install)
|
||||
aur_build() {
|
||||
as_user bash -c '
|
||||
set -e
|
||||
cd ~ && git clone -q "https://aur.archlinux.org/$1.git" && cd "$1"
|
||||
if [ "${2:-}" = older ]; then
|
||||
cur=$(sed -n "s/^\tpkgver = //p" .SRCINFO)
|
||||
for c in $(git log --format=%h -- .SRCINFO); do
|
||||
v=$(git show "$c:.SRCINFO" | sed -n "s/^\tpkgver = //p")
|
||||
if [ "$v" != "$cur" ]; then git checkout -q "$c"; break; fi
|
||||
done
|
||||
fi
|
||||
makepkg -si --noconfirm >/dev/null 2>&1
|
||||
' _ "$@" || { echo "aur_build $* failed"; exit 1; }
|
||||
}
|
||||
# paru-bin 2.1.0 links libalpm.so.15 and does not start with pacman 7.1 (libalpm.so.16):
|
||||
# "error while loading shared libraries: libalpm.so.15" (verified) - build paru from source
|
||||
aur_build "${PARU_PKG:-paru}"
|
||||
aur_build yay-bin
|
||||
aur_build pfetch older
|
||||
show paru --version
|
||||
show yay --version
|
||||
show -r pacman -Qm
|
||||
|
||||
step "runuser -u: the environment it sets"
|
||||
show -r runuser -u "$U" -- env
|
||||
show -r runuser -u "$U" -- id
|
||||
|
||||
step "a foreign package that is not in the AUR (local build)"
|
||||
w=$(mktemp -d)
|
||||
mkdir -p "$w/usr/share/su-local-only"
|
||||
echo 1.0 >"$w/usr/share/su-local-only/VERSION"
|
||||
printf 'pkgname = su-local-only\npkgbase = su-local-only\npkgver = 1.0-1\npkgdesc = local only\narch = any\nsize = 4\n' >"$w/.PKGINFO"
|
||||
(cd "$w" && bsdtar --zstd -cf /tmp/su-local-only-1.0-1-any.pkg.tar.zst .PKGINFO usr)
|
||||
pacman -U --noconfirm /tmp/su-local-only-1.0-1-any.pkg.tar.zst >/dev/null 2>&1
|
||||
show -r pacman -Qm
|
||||
|
||||
step "-Qua (piped, stdin /dev/null)"
|
||||
show paru -Qua
|
||||
show yay -Qua
|
||||
show paru -Qu
|
||||
show yay -Qu
|
||||
|
||||
step "-Qua with IgnorePkg = pfetch"
|
||||
sed -i '0,/^\[options\]/s||[options]\nIgnorePkg = pfetch|' /etc/pacman.conf
|
||||
show paru -Qua
|
||||
show yay -Qua
|
||||
sed -i '/^IgnorePkg = pfetch$/d' /etc/pacman.conf
|
||||
|
||||
step "-Si / -Sia of an AUR package, compared with -Qi"
|
||||
show -r env COLUMNS=0 pacman -Qi pfetch
|
||||
show paru -Si pfetch
|
||||
show paru -Sia pfetch
|
||||
show paru -Si --aur pfetch
|
||||
show yay -Si pfetch
|
||||
show yay -Sia pfetch
|
||||
show paru -Sia no-such-package-zzz
|
||||
show yay -Sia no-such-package-zzz
|
||||
show paru -Si bash
|
||||
|
||||
step "-Qua as root"
|
||||
show -r paru -Qua
|
||||
show -r yay -Qua
|
||||
|
||||
step "AUR unreachable (aur.archlinux.org -> 127.0.0.1)"
|
||||
cp /etc/hosts /tmp/hosts.orig
|
||||
echo '127.0.0.1 aur.archlinux.org' >>/etc/hosts
|
||||
show paru -Qua
|
||||
show yay -Qua
|
||||
show paru -Sia pfetch
|
||||
show yay -Sia pfetch
|
||||
cat /tmp/hosts.orig >/etc/hosts
|
||||
|
||||
# reset_pfetch: back to the outdated pfetch built at the start (makepkg left the package file)
|
||||
reset_pfetch() { pacman -U --noconfirm "$H"/pfetch/pfetch-[0-9]*.pkg.tar.zst >/dev/null 2>&1; pacman -Q pfetch; }
|
||||
|
||||
step "-Sua as root (pfetch outdated)"
|
||||
show -r paru -Sua --noconfirm
|
||||
show -r yay -Sua --noconfirm
|
||||
show -r pacman -Q pfetch
|
||||
|
||||
step "-Sua without --noconfirm, stdin /dev/null (no terminal)"
|
||||
show paru -Sua
|
||||
show -r pacman -Q pfetch
|
||||
reset_pfetch
|
||||
show yay -Sua
|
||||
show -r pacman -Q pfetch
|
||||
reset_pfetch
|
||||
|
||||
step "-Sua --ignore (non-interactive: --noconfirm, stdin /dev/null)"
|
||||
show paru -Sua --noconfirm --ignore pfetch
|
||||
show -r pacman -Q pfetch
|
||||
show yay -Sua --noconfirm --ignore pfetch
|
||||
show -r pacman -Q pfetch
|
||||
show paru -Sua --noconfirm --ignore su-dummy-x,su-dummy-y
|
||||
show -r pacman -Q pfetch
|
||||
reset_pfetch
|
||||
show yay -Sua --noconfirm
|
||||
show -r pacman -Q pfetch
|
||||
|
||||
step "devel (-git) packages: pfetch-git (conflicts with pfetch, which goes first)"
|
||||
show -r pacman -Rns --noconfirm pfetch
|
||||
show paru -S --noconfirm pfetch-git
|
||||
show -r pacman -Q pfetch-git
|
||||
show paru --gendb
|
||||
show yay -Y --gendb
|
||||
for f in "$H/.local/state/paru/devel.toml" "$H/.cache/yay/vcs.json"; do
|
||||
echo "--- $f"; cat "$f"; echo
|
||||
sed -i -E 's/[0-9a-f]{40}/0000000000000000000000000000000000000000/g' "$f"
|
||||
echo "--- $f with every commit id replaced by zeros (an older upstream state)"; cat "$f"; echo
|
||||
done
|
||||
show paru -Qua
|
||||
show paru -Qua --devel
|
||||
show yay -Qua
|
||||
show yay -Qua --devel
|
||||
show paru -Sua --noconfirm
|
||||
show -r pacman -Q pfetch-git
|
||||
show paru -Sua --noconfirm --devel
|
||||
show -r pacman -Q pfetch-git
|
||||
|
||||
step "-Qua with nothing to upgrade"
|
||||
show paru -Qua
|
||||
show yay -Qua
|
||||
show paru -Qua --devel
|
||||
show yay -Qua --devel
|
||||
echo "ALL DONE"
|
||||
+63
-4
@@ -20,13 +20,44 @@ Order (first match wins, overridable with `--backend=<name>`):
|
||||
|
||||
| backend | detected by | notes |
|
||||
|----------|-----------------------------|--------------------------------------------|
|
||||
| `pacman` | `pacman` in PATH | `pman` present => label shown as `pman`, same commands; honor pacman `IgnorePkg` (`pacman -Qu` marks them `[ignored]`); the refresh syncs a throwaway `--dbpath` copy; at `<enter>` its reviewed databases are installed and applied with `pacman -Su` |
|
||||
| `pacman` | `pacman` in PATH | `pman` present => label shown as `pman`, same commands; honor pacman `IgnorePkg` (`pacman -Qu` marks them `[ignored]`); the refresh syncs a throwaway `--dbpath` copy; at `<enter>` its reviewed databases are installed and applied with `pacman -Su`; AUR packages through `paru` (else `yay`) when installed, see "AUR" below |
|
||||
| `apt` | `apt-get` in PATH | Debian/Ubuntu |
|
||||
| `dnf` | `dnf` in PATH (else `yum`) | dnf4 (EL9) and dnf5 (EL10) differences hidden in the backend |
|
||||
| `zypper` | `zypper` in PATH | `dist-upgrade` on Tumbleweed (ID=opensuse-tumbleweed), `update` on Leap; on Tumbleweed the review list is the `dist-upgrade --dry-run` plan |
|
||||
|
||||
Each backend implements the same function set (see "Backend contract").
|
||||
|
||||
### AUR (pacman backend only)
|
||||
|
||||
| helper | detected by | overridden by |
|
||||
|--------|-------------|---------------|
|
||||
| `paru` | `paru` in PATH (first) | `--aur-helper=paru` (an error when it is not in PATH) |
|
||||
| `yay` | `yay` in PATH (when there is no paru) | `--aur-helper=yay` (idem) |
|
||||
| none | neither installed | `--aur-helper=none`, `--no-aur` |
|
||||
|
||||
The helper is detected with the backend (before any sudo re-exec). The banner then reads
|
||||
`Backend: pman (Arch Linux) + AUR via paru`. The other backends never look a helper up:
|
||||
`--aur-helper=paru|yay` is accepted there and ignored with a warning (`--aur-helper=paru is
|
||||
ignored: AUR helpers only apply to the pacman backend ...`), `--no-aur`/`--aur-helper=none` silently.
|
||||
|
||||
The helper never runs as root (paru refuses to install AUR packages as root, makepkg refuses to
|
||||
build as root). Decided once, after the root handling (the banner decides it; the listing runs in
|
||||
subshells):
|
||||
|
||||
| we are | the helper runs |
|
||||
|--------|-----------------|
|
||||
| not root (`list --no-refresh`, `info`, `show`, `--dry-run`) | as we are: `paru ARGS` |
|
||||
| root, `SUDO_USER` names a user whose uid (`getent passwd`, else `/etc/passwd`) is not 0 - our sudo re-exec, or any sudo | as that user, from that user's home: `runuser -u "$SUDO_USER" -- env HOME=<home> paru ARGS`, `sudo -u "$SUDO_USER" -- env HOME=<home> ...` when there is no runuser |
|
||||
| root without `SUDO_USER` (a root login), `SUDO_USER` root or unknown, neither runuser nor sudo | not at all: no AUR row, one warning (`aur: paru is never run as root and there is no invoking user (SUDO_USER is not set): AUR packages are neither listed nor upgraded ...`) |
|
||||
|
||||
Helper commands (formats verified in `discovery/pacman.md` section 15, paru 2.1.0 and yay 13.0.1):
|
||||
|
||||
| need | command | notes |
|
||||
|------|---------|-------|
|
||||
| list | `<helper> -Qua` | `name old -> new`, then ` [ignored]` (paru; yay leaves `IgnorePkg` packages out) or yay's age (` [122d13h]`, ignored); devel packages show `latest-commit` as the new version; escape sequences and other lines are dropped. Exit 1 with nothing on stdout and stderr = nothing to upgrade; any other failure: the helper's stderr lines as warnings, then `aur: <helper> -Qua failed, AUR packages not listed`, and no AUR row. Asked on every run, also with `--no-refresh` (the helper always compares with the AUR RPC) |
|
||||
| `N??` candidate | `<helper> -Sia PKG` | for a package in no repository (`pacman -Sl`); paru's record aligns its keys like `pacman -Qi`, yay's are wider (re-padded by the merge) |
|
||||
| upgrade | `<helper> -Sua [--noconfirm] [--ignore a,b]` | see "After `<enter>`" |
|
||||
|
||||
## Statefile
|
||||
|
||||
Directory: `/var/lib/skeptical-updater/` (override: `SKEPTICAL_UPDATER_STATE_DIR`).
|
||||
@@ -108,6 +139,19 @@ Command grammar (one line, whitespace-separated tokens, each `N` is the number i
|
||||
| `q` | quit without upgrading (exit 0) |
|
||||
| `h`, `?` | help |
|
||||
|
||||
List format: a marker column (`-` excluded, `=` trusted with `--all`, else a space), the number in
|
||||
brackets right-aligned to the widest one, then one column that is `*` for an AUR row and a space for
|
||||
every other row (on every backend: the columns stay aligned), a space, then name, old version, `->`,
|
||||
new version, repository (`aur` for AUR rows) and the flags (`[held]`):
|
||||
|
||||
```
|
||||
[1] su-dummy-a 1.0-1 -> 2.0-1 su-dummy
|
||||
-[2]* su-dummy-aur 1.0-1 -> 2.0-1 aur
|
||||
```
|
||||
|
||||
On AUR rows `N?`, `N+` and `Nx` act through pacman like on any installed package; `N??` merges
|
||||
`pacman -Qi` with `<helper> -Sia PKG`; `N-` excludes the row through the helper's `--ignore`.
|
||||
|
||||
Numbers are stable for the whole session (removed rows leave a gap, never renumber).
|
||||
All tokens of a line are validated first: an unknown token or a number that is not listed
|
||||
prints an error, runs nothing of that line and re-prompts. EOF on stdin aborts without
|
||||
@@ -142,7 +186,7 @@ ones (dnf5: 16 installed, 15 available), the candidate lines are re-padded to th
|
||||
|
||||
| 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) |
|
||||
| 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, `<helper> -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=<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) |
|
||||
@@ -188,6 +232,17 @@ After `<enter>`:
|
||||
plain `pacman -Su` applies the existing databases. Throwaway databases carry their owner's
|
||||
pid in the name (`/tmp/skeptical-updater-db.<pid>.XXXXXX`); those of runs that are gone are
|
||||
removed at the start of the next root run.
|
||||
AUR rows (pacman backend): pacman gets `--ignore` with the excluded repository rows only. When
|
||||
no repository row is left to upgrade (every one excluded or `[held]`, no pending note) but AUR
|
||||
rows are, (a)-(c) are skipped ("No repository package to upgrade: pacman is not run, only
|
||||
paru."). Otherwise (a)-(c) run first, and only after they succeeded (d)
|
||||
`<helper> -Sua --ignore <every excluded row, repository and AUR>` runs as the invoking user (see
|
||||
"AUR"), when an AUR row that is neither excluded nor `[held]` remains: with stdin a terminal the
|
||||
helper gets it and asks as usual (paru's PKGBUILD review, yay's menus, its sudo password);
|
||||
otherwise `--noconfirm` and stdin from `/dev/null` (yay exits 1 at its first menu on a closed
|
||||
stdin). A helper failure is reported as `the AUR upgrade failed: paru exited with status N (the
|
||||
repository packages were upgraded)` and its status is the exit status; `--dry-run` prints the
|
||||
helper's command line.
|
||||
4. Restore holds/locks, exit with the package manager's exit code.
|
||||
|
||||
Trusted packages are upgraded silently along with everything else; they are only
|
||||
@@ -215,6 +270,8 @@ options:
|
||||
--dry-run print native commands instead of running mutating ones
|
||||
--state-dir=DIR override /var/lib/skeptical-updater
|
||||
--no-color disable ANSI colours (also honours NO_COLOR, non-tty stdout)
|
||||
--aur-helper=NAME pacman backend: paru|yay|none (default: paru, else yay, when installed)
|
||||
--no-aur same as --aur-helper=none
|
||||
--version, --help
|
||||
```
|
||||
|
||||
@@ -236,7 +293,8 @@ Every backend is a set of bash functions with a common prefix. All output is
|
||||
|
||||
```
|
||||
<be>_refresh # update metadata; non-fatal on failure (warn)
|
||||
<be>_upgradable # stdout: name<TAB>old<TAB>new<TAB>repo<TAB>flags (flags: "" | "held"; old "-" = new package)
|
||||
<be>_upgradable # stdout: name<TAB>old<TAB>new<TAB>repo<TAB>flags (flags: space-separated words:
|
||||
# "held", "aur" (pacman: an AUR row, shown with "*"); old "-" = new package)
|
||||
# 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)
|
||||
@@ -248,7 +306,8 @@ Every backend is a set of bash functions with a common prefix. All output is
|
||||
<be>_purge_plan PKG # stdout: every package a purge of PKG and its now-unneeded dependencies removes
|
||||
# (simulation, no cascade); non-zero = refused
|
||||
<be>_purge PKG... # non-interactive native purge/remove of exactly the planned packages
|
||||
<be>_upgrade EXCLUDED... # non-interactive native upgrade skipping EXCLUDED (handles hold/lock save+restore)
|
||||
<be>_upgrade EXCLUDED... # non-interactive native upgrade skipping EXCLUDED (handles hold/lock save+restore;
|
||||
# pacman: then the AUR helper, see "After <enter>")
|
||||
<be>_label # "apt" / "pman" / ...
|
||||
```
|
||||
|
||||
|
||||
+263
-17
@@ -52,6 +52,7 @@ opt_dry_run=0
|
||||
opt_color=1
|
||||
opt_state_dir=
|
||||
opt_state_dir_set=0
|
||||
opt_aur= # AUR helper (pacman backend): '' = detect paru, then yay; paru | yay | none
|
||||
cmd=
|
||||
cmd_args=()
|
||||
|
||||
@@ -100,6 +101,9 @@ options:
|
||||
--dry-run print native commands instead of running mutating ones
|
||||
--state-dir=DIR override /var/lib/skeptical-updater (env: SKEPTICAL_UPDATER_STATE_DIR)
|
||||
--no-color disable ANSI colours (also honours NO_COLOR and non-tty stdout)
|
||||
--aur-helper=NAME pacman backend: list and upgrade AUR packages with paru or yay, or
|
||||
none (default: paru, else yay, when installed; other backends ignore it)
|
||||
--no-aur same as --aur-helper=none
|
||||
-h, --help this help
|
||||
-V, --version print the version
|
||||
|
||||
@@ -120,6 +124,10 @@ interactive commands (one line, whitespace-separated tokens, N = number in brack
|
||||
h, ? this help
|
||||
end of input abort without upgrading (exit 2)
|
||||
|
||||
A '*' right after the number marks an AUR package (pacman backend with paru or yay): <enter>
|
||||
upgrades the repository packages with pacman first, then the AUR ones with the helper, run as
|
||||
the user who invoked sudo; on a terminal the helper asks as usual (PKGBUILD review).
|
||||
|
||||
exit status: 0 ok, 1 usage/backend error, 2 aborted, otherwise the package
|
||||
manager's exit status from the upgrade step.
|
||||
EOF
|
||||
@@ -136,7 +144,7 @@ session_help() {
|
||||
pacman -Rns; purge the dependents first
|
||||
N+ trust N (and its installed dependencies) forever
|
||||
*+ *- trust / exclude every listed package (*- again re-includes them)
|
||||
l redraw the list
|
||||
l redraw the list (a '*' after the number marks an AUR package)
|
||||
q quit without upgrading
|
||||
h ? this help
|
||||
Several tokens can be given on one line, e.g. "1? 3- 4+".
|
||||
@@ -171,6 +179,16 @@ parse_args() {
|
||||
opt_state_dir_set=1
|
||||
shift
|
||||
;;
|
||||
--aur-helper=*)
|
||||
opt_aur=${a#*=}
|
||||
[[ -n $opt_aur ]] || usage_error "--aur-helper needs a value"
|
||||
;;
|
||||
--aur-helper)
|
||||
(($#)) || usage_error "--aur-helper needs a value"
|
||||
opt_aur=$1
|
||||
shift
|
||||
;;
|
||||
--no-aur) opt_aur=none ;;
|
||||
--all) opt_all=1 ;;
|
||||
--no-refresh) opt_refresh=0 ;;
|
||||
--dry-run) opt_dry_run=1 ;;
|
||||
@@ -209,6 +227,10 @@ parse_args() {
|
||||
*) usage_error "unknown backend '$opt_backend' (expected apt, dnf, pacman or zypper)" ;;
|
||||
esac
|
||||
fi
|
||||
case $opt_aur in
|
||||
'' | paru | yay | none) ;;
|
||||
*) usage_error "unknown AUR helper '$opt_aur' (expected paru, yay or none)" ;;
|
||||
esac
|
||||
if ((opt_state_dir_set)); then
|
||||
STATE_DIR=$opt_state_dir
|
||||
else
|
||||
@@ -391,7 +413,9 @@ fmt_cmd() { # the command line, shell-quoted where needed
|
||||
|
||||
# run_child CMD...: run CMD with stdin from /dev/null and our lock descriptors closed (a daemon
|
||||
# started by a maintainer script must not inherit, and so keep, our flock).
|
||||
run_child() {
|
||||
run_child() { run_child_stdin "$@" </dev/null; }
|
||||
# run_child_stdin CMD...: the same, but CMD keeps our stdin (an AUR helper asking on a terminal)
|
||||
run_child_stdin() {
|
||||
(
|
||||
local v fd
|
||||
for v in ${SU_LOCK[@]+"${SU_LOCK[@]}"}; do
|
||||
@@ -401,7 +425,7 @@ run_child() {
|
||||
fi
|
||||
done
|
||||
exec "$@"
|
||||
) </dev/null
|
||||
)
|
||||
}
|
||||
|
||||
# run_cmd CMD...: run a MUTATING native command (output streamed); under --dry-run print
|
||||
@@ -1115,7 +1139,10 @@ pacman_exit_cleanup() {
|
||||
PACMAN_DBS_PENDING=0
|
||||
}
|
||||
|
||||
pacman_init() { have pacman || die "pacman backend: pacman not found in PATH"; }
|
||||
pacman_init() {
|
||||
have pacman || die "pacman backend: pacman not found in PATH"
|
||||
aur_detect
|
||||
}
|
||||
pacman_label() { if have pman; then printf 'pman'; else printf 'pacman'; fi; }
|
||||
_pm() { COLUMNS=0 pacman ${PM_DB[@]+"${PM_DB[@]}"} "$@" </dev/null; }
|
||||
pacman_dbpath() {
|
||||
@@ -1175,6 +1202,151 @@ pacman_refresh() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---- AUR (pacman backend only): paru or yay ---------------------------------------------------------
|
||||
# discovery/pacman.md section 15. The helper lists (-Qua), describes (-Sia) and upgrades (-Sua) the
|
||||
# AUR packages; everything about installed packages (N?, N+, Nx) stays with pacman. It never runs as
|
||||
# root (makepkg refuses to build as root): when we are root through our sudo re-exec it runs as the
|
||||
# invoking user ($SUDO_USER, with that user's HOME and from that home); unprivileged (list
|
||||
# --no-refresh, info, show, --dry-run) it runs as we are; a root login (no SUDO_USER) gets no AUR.
|
||||
AUR_HELPER= # paru | yay; '' = no AUR helper in use
|
||||
AUR_STATE= # '' = not decided yet | on | off (aur_prepare)
|
||||
AUR_RUN=() # prefix running the helper as the invoking user: runuser|sudo -u USER -- env HOME=...
|
||||
AUR_HOME= # the helper's working directory (that user's home) when AUR_RUN is set
|
||||
AUR_PENDING_WARN= # why the AUR is off, printed right after the banner
|
||||
PW_UID='' PW_HOME='' # passwd_entry results
|
||||
|
||||
# aur_detect (pacman_init, i.e. before any sudo re-exec): --aur-helper/--no-aur, else paru, then yay;
|
||||
# a helper asked for by name that is not installed is an error
|
||||
aur_detect() {
|
||||
AUR_HELPER=
|
||||
case $opt_aur in
|
||||
none) ;;
|
||||
paru | yay)
|
||||
have "$opt_aur" || die "--aur-helper=$opt_aur: $opt_aur not found in PATH"
|
||||
AUR_HELPER=$opt_aur
|
||||
;;
|
||||
*) if have paru; then AUR_HELPER=paru; elif have yay; then AUR_HELPER=yay; fi ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# passwd_entry USER: PW_UID and PW_HOME of USER (getent, else /etc/passwd); 1 if unknown
|
||||
passwd_entry() {
|
||||
local line='' name x uid home
|
||||
if have getent; then
|
||||
line=$(getent passwd "$1" 2>/dev/null) || line=''
|
||||
elif [[ -r /etc/passwd ]]; then
|
||||
while IFS= read -r x; do
|
||||
if [[ ${x%%:*} == "$1" ]]; then
|
||||
line=$x
|
||||
break
|
||||
fi
|
||||
done </etc/passwd
|
||||
fi
|
||||
IFS=: read -r name x uid x x home x <<<"$line"
|
||||
[[ $name == "$1" && $uid =~ ^[0-9]+$ && -n $home ]] || return 1
|
||||
PW_UID=$uid PW_HOME=$home
|
||||
}
|
||||
|
||||
# aur_prepare [quiet]: decide once how the helper runs; 1 = no AUR this run. The reason for "no AUR"
|
||||
# is warned about once: right away, or with "quiet" after the banner (aur_warn_pending); never from a
|
||||
# subshell (the listing runs in one: the banner decides first).
|
||||
aur_prepare() {
|
||||
case $AUR_STATE in on) return 0 ;; off) return 1 ;; esac
|
||||
[[ -n $AUR_HELPER ]] || {
|
||||
AUR_STATE=off
|
||||
return 1
|
||||
}
|
||||
if ((SU_EUID != 0)); then
|
||||
AUR_RUN=() AUR_HOME='' AUR_STATE=on
|
||||
return 0
|
||||
fi
|
||||
local u=${SUDO_USER:-} why='' re='^[A-Za-z0-9_][A-Za-z0-9_.-]*[$]?$'
|
||||
if [[ -z $u ]]; then
|
||||
why="$AUR_HELPER is never run as root and there is no invoking user (SUDO_USER is not set)"
|
||||
elif [[ ! $u =~ $re ]] || ! passwd_entry "$u"; then
|
||||
why="the invoking user '$u' (SUDO_USER) is unknown"
|
||||
elif ((PW_UID == 0)); then
|
||||
why="$AUR_HELPER is never run as root and the invoking user ($u) is root"
|
||||
elif have runuser; then
|
||||
AUR_RUN=(runuser -u "$u" -- env HOME="$PW_HOME")
|
||||
elif have sudo; then
|
||||
AUR_RUN=(sudo -u "$u" -- env HOME="$PW_HOME")
|
||||
else
|
||||
why="neither runuser nor sudo is available to run $AUR_HELPER as $u"
|
||||
fi
|
||||
if [[ -n $why ]]; then
|
||||
AUR_STATE=off
|
||||
why="aur: $why: AUR packages are neither listed nor upgraded (run $SU_PROG through sudo from your own account, or use --no-aur)"
|
||||
if [[ ${1:-} == quiet ]]; then
|
||||
AUR_PENDING_WARN=$why
|
||||
elif ((BASH_SUBSHELL == 0)); then
|
||||
warn "$why"
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
AUR_HOME=$PW_HOME AUR_STATE=on
|
||||
}
|
||||
aur_warn_pending() {
|
||||
[[ -z $AUR_PENDING_WARN ]] || warn "$AUR_PENDING_WARN"
|
||||
AUR_PENDING_WARN=
|
||||
}
|
||||
|
||||
# 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
|
||||
aur_exec() {
|
||||
(
|
||||
if [[ -n $AUR_HOME ]]; then cd -- "$AUR_HOME" 2>/dev/null || cd /; fi
|
||||
run_child ${AUR_RUN[@]+"${AUR_RUN[@]}"} "$AUR_HELPER" "$@"
|
||||
)
|
||||
}
|
||||
|
||||
# aur_upgradable: AUR rows (name<TAB>old<TAB>new<TAB>aur<TAB>aur[ held]) from `<helper> -Qua`:
|
||||
# "name old -> new", then " [ignored]" (paru; yay leaves IgnorePkg packages out) or yay 13's time
|
||||
# since the AUR change (" [122d13h]"). The helper asks the AUR RPC every time (no refresh needed).
|
||||
# Exit 1 with nothing on stdout and stderr = nothing to upgrade; any other failure (the AUR is
|
||||
# unreachable) is warned about with the helper's messages, and no AUR row is listed.
|
||||
aur_upgradable() {
|
||||
local out rc=0 errf err='' l
|
||||
errf=$(mktemp "${TMPDIR:-/tmp}/$SU_PROG.XXXXXX") || errf=/dev/null
|
||||
out=$(aur_exec -Qua 2>"$errf") || rc=$?
|
||||
if [[ $errf != /dev/null ]]; then
|
||||
err=$(cat -- "$errf")
|
||||
rm -f -- "$errf"
|
||||
fi
|
||||
if ((rc)); then
|
||||
[[ $rc -ne 1 || -n $out || -n ${err//[[:space:]]/} ]] || return 0
|
||||
while IFS= read -r l; do [[ -z ${l//[[:space:]]/} ]] || warn "$AUR_HELPER: $l"; done <<<"$err"
|
||||
warn "aur: $AUR_HELPER -Qua failed, AUR packages not listed"
|
||||
return 0
|
||||
fi
|
||||
awk '{ gsub(/\033\[[0-9;]*m/, "") }
|
||||
NF >= 4 && $3 == "->" && $1 ~ /^[A-Za-z0-9_+@][A-Za-z0-9_+.@-]*$/ {
|
||||
printf "%s\t%s\t%s\taur\taur%s\n", $1, $2, $4, (($5 == "[ignored]") ? " held" : "")
|
||||
}' <<<"$out"
|
||||
}
|
||||
|
||||
# aur_info PKG: the AUR record of PKG, pacman-style "Key : Value" lines (paru: keys padded like
|
||||
# pacman's, yay: wider; one record either way). -Sia: plain `yay -Si` only asks the repositories.
|
||||
aur_info() { aur_exec -Sia "$1" 2>/dev/null; }
|
||||
|
||||
# aur_upgrade EXCLUDED...: `<helper> -Sua [--noconfirm] [--ignore a,b]` as the invoking user. With a
|
||||
# terminal on stdin the helper gets it and asks as usual (its PKGBUILD review); otherwise
|
||||
# --noconfirm, stdin from /dev/null. --dry-run prints the command line.
|
||||
aur_upgrade() {
|
||||
local -a c=(${AUR_RUN[@]+"${AUR_RUN[@]}"} "$AUR_HELPER" -Sua)
|
||||
[[ -t 0 ]] || c+=(--noconfirm)
|
||||
if (($#)); then c+=(--ignore "$(join_by , "$@")"); fi
|
||||
if ((opt_dry_run)); then
|
||||
say "+ $(fmt_cmd "${c[@]}")"
|
||||
return 0
|
||||
fi
|
||||
printf '%s+ %s%s\n' "$C_DIM" "$(fmt_cmd "${c[@]}")" "$C_RESET" 2>/dev/null || true
|
||||
(
|
||||
if [[ -n $AUR_HOME ]]; then cd -- "$AUR_HOME" 2>/dev/null || cd /; fi
|
||||
if [[ -t 0 ]]; then run_child_stdin "${c[@]}"; else run_child "${c[@]}"; fi
|
||||
)
|
||||
}
|
||||
|
||||
# discovery/pacman/lib.sh pm_upgradable: -Qu joined with the first -Sl repo; [ignored] => held.
|
||||
# FILENAME == ARGV[1] (not FNR == NR): correct also when `pacman -Sl` prints nothing.
|
||||
pacman_upgradable() {
|
||||
@@ -1191,6 +1363,13 @@ pacman_upgradable() {
|
||||
' <(printf '%s\n' "$sl") <(printf '%s\n' "$quraw"))
|
||||
[[ -z $qu ]] || printf '%s\n' "$qu"
|
||||
while IFS= read -r n; do [[ -n $n ]] && listed[${n%%$'\t'*}]=1; done <<<"$qu"
|
||||
# AUR rows; a name the repositories list wins (a package that moved from the AUR into a
|
||||
# repository, while the system's sync database the helper reads does not know that yet)
|
||||
if aur_prepare; then
|
||||
while IFS= read -r n; do
|
||||
[[ -n $n && -z ${listed[${n%%$'\t'*}]+x} ]] && printf '%s\n' "$n"
|
||||
done < <(aur_upgradable)
|
||||
fi
|
||||
# what the upgrade really installs: new dependencies and replacements are not in -Qu
|
||||
sup=$(_pm -Sup --print-format '%n' 2>>"$errf") || sup=''
|
||||
while IFS= read -r n; do
|
||||
@@ -1236,20 +1415,36 @@ 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() {
|
||||
local ok=1
|
||||
_pm -Qi -- "$1" 2>/dev/null && ok=0
|
||||
_pm -Si -- "$1" 2>/dev/null && ok=0
|
||||
local ok=1 qi si aur='' l
|
||||
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"
|
||||
if [[ -n $aur ]]; then
|
||||
while IFS= read -r l; do printf '> %s\n' "$l"; done <<<"$aur"
|
||||
ok=0
|
||||
fi
|
||||
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)
|
||||
# take it from (the first one `-Sl` lists it in, as in pacman_upgradable); in no repository, the
|
||||
# AUR record when a helper is in use
|
||||
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
|
||||
if [[ -n $repo ]]; then
|
||||
_pm -Si -- "$repo/$1" 2>/dev/null
|
||||
return
|
||||
fi
|
||||
aur_prepare || return 1
|
||||
aur_info "$1"
|
||||
}
|
||||
pacman_rdepends() {
|
||||
local out w
|
||||
@@ -1406,7 +1601,38 @@ pacman_install_dbs() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# pacman_upgrade EXCLUDED...: the repository packages with pacman (pacman_upgrade_repo), then - when
|
||||
# AUR rows remain to upgrade - the AUR packages with the helper (`-Sua --ignore EXCLUDED`), only after
|
||||
# a successful repository step. With nothing for pacman to do but AUR rows to upgrade, pacman is not
|
||||
# run at all.
|
||||
pacman_upgrade() {
|
||||
local i n rc=0 n_repo=0 n_aur=0 ran=0
|
||||
local -a repo_ex=()
|
||||
local -A aur=()
|
||||
for ((i = 0; i < ${#R_NAME[@]}; i++)); do
|
||||
((R_GONE[i] == 0)) || continue
|
||||
if ((R_AUR[i])); then aur[${R_NAME[i]}]=1; fi
|
||||
((R_EXCL[i] == 0)) && [[ ${R_FLAGS[i]} != held ]] || continue
|
||||
if ((R_AUR[i])); then n_aur=$((n_aur + 1)); else n_repo=$((n_repo + 1)); fi
|
||||
done
|
||||
for n in "$@"; do [[ -n ${aur[$n]+x} ]] || repo_ex+=("$n"); done
|
||||
if ((n_repo || !n_aur || ${#R_NOTES[@]})); then
|
||||
pacman_upgrade_repo ${repo_ex[@]+"${repo_ex[@]}"} || return $?
|
||||
ran=1
|
||||
else
|
||||
say "No repository package to upgrade: pacman is not run, only $AUR_HELPER."
|
||||
fi
|
||||
((n_aur)) || return 0
|
||||
aur_upgrade "$@" || rc=$?
|
||||
if ((rc)); then
|
||||
UPGRADE_FAILED_MSG="the AUR upgrade failed: $AUR_HELPER exited with status $rc"
|
||||
((ran == 0)) || UPGRADE_FAILED_MSG+=" (the repository packages were upgraded)"
|
||||
fi
|
||||
return "$rc"
|
||||
}
|
||||
|
||||
# pacman_upgrade_repo EXCLUDED...: the reviewed repository transaction (see the top of this section)
|
||||
pacman_upgrade_repo() {
|
||||
local rc=0 real tmp out
|
||||
local -a ign=()
|
||||
if (($#)); then ign=(--ignore "$(join_by , "$@")"); fi
|
||||
@@ -1982,27 +2208,33 @@ zypper_upgrade() {
|
||||
# commands
|
||||
# ===================================================================================================
|
||||
banner() {
|
||||
local pretty
|
||||
local pretty via=''
|
||||
pretty=$(os_release_field PRETTY_NAME)
|
||||
say "${C_BOLD}Backend: $(be label)${pretty:+ ($pretty)}${C_RESET}"
|
||||
# pacman: the AUR helper in use (decided here, once: the listing runs in subshells)
|
||||
if [[ $BACKEND == pacman ]] && aur_prepare quiet; then via=" + AUR via $AUR_HELPER"; fi
|
||||
say "${C_BOLD}Backend: $(be label)${pretty:+ ($pretty)}$via${C_RESET}"
|
||||
if [[ $BACKEND == pacman ]]; then aur_warn_pending; fi
|
||||
}
|
||||
|
||||
maybe_refresh() { if ((opt_refresh)); then be refresh || true; fi; }
|
||||
|
||||
# rows of the review list: R_* arrays indexed 0..n-1, sorted by name; R_NOTES: what the backend
|
||||
# reports beyond the rows (new dependencies, removals, pending changes it does not list)
|
||||
R_NAME=() R_OLD=() R_NEW=() R_REPO=() R_FLAGS=() R_NUM=() R_GONE=() R_EXCL=() R_NOTES=()
|
||||
# R_FLAGS: what is shown in brackets ("held"); R_AUR: 1 for an AUR package (flag "aur" from the
|
||||
# backend; shown as a "*" right after the number)
|
||||
R_NAME=() R_OLD=() R_NEW=() R_REPO=() R_FLAGS=() R_AUR=() R_NUM=() R_GONE=() R_EXCL=() R_NOTES=()
|
||||
N_IDX=() # number -> row index
|
||||
N_MAX=0
|
||||
|
||||
load_rows() {
|
||||
local out rc=0 line name old new repo flags i n=0
|
||||
local out rc=0 line name old new repo flags i n=0 w aur shown
|
||||
local -a fw=()
|
||||
out=$(be upgradable | sort -t $'\t' -k1,1 -u) || rc=$?
|
||||
if ((rc)); then
|
||||
err "could not list the upgradable packages ($(be label) exited $rc)"
|
||||
exit 1
|
||||
fi
|
||||
R_NAME=() R_OLD=() R_NEW=() R_REPO=() R_FLAGS=() R_NUM=() R_GONE=() R_EXCL=() N_IDX=() R_NOTES=()
|
||||
R_NAME=() R_OLD=() R_NEW=() R_REPO=() R_FLAGS=() R_AUR=() R_NUM=() R_GONE=() R_EXCL=() N_IDX=() R_NOTES=()
|
||||
while IFS= read -r line; do
|
||||
[[ -n $line ]] || continue
|
||||
if [[ $line == '#'* ]]; then
|
||||
@@ -2011,7 +2243,12 @@ load_rows() {
|
||||
fi
|
||||
IFS=$US read -r name old new repo flags <<<"${line//$'\t'/$US}"
|
||||
[[ -n $name ]] || continue
|
||||
R_NAME+=("$name") R_OLD+=("${old:--}") R_NEW+=("${new:--}") R_REPO+=("${repo:--}") R_FLAGS+=("$flags")
|
||||
aur=0 shown=''
|
||||
read -r -a fw <<<"$flags" || true
|
||||
for w in ${fw[@]+"${fw[@]}"}; do
|
||||
if [[ $w == aur ]]; then aur=1; else shown+=${shown:+ }$w; fi
|
||||
done
|
||||
R_NAME+=("$name") R_OLD+=("${old:--}") R_NEW+=("${new:--}") R_REPO+=("${repo:--}") R_FLAGS+=("$shown") R_AUR+=("$aur")
|
||||
R_GONE+=(0) R_EXCL+=(0)
|
||||
done <<<"$out"
|
||||
for ((i = 0; i < ${#R_NAME[@]}; i++)); do
|
||||
@@ -2039,7 +2276,7 @@ row_visible() { # row_visible IDX
|
||||
}
|
||||
|
||||
print_list() {
|
||||
local i total=0 trusted=0 review=0 namew=4 oldw=3 neww=3 numw marker line flags repo note
|
||||
local i total=0 trusted=0 review=0 namew=4 oldw=3 neww=3 numw marker aurm line flags repo note
|
||||
for ((i = 0; i < ${#R_NAME[@]}; i++)); do
|
||||
((R_GONE[i] == 0)) || continue
|
||||
total=$((total + 1))
|
||||
@@ -2065,11 +2302,14 @@ print_list() {
|
||||
elif is_trusted "${R_NAME[i]}"; then
|
||||
marker="${C_DIM}=${C_RESET}"
|
||||
fi
|
||||
# an AUR package: "*" right after the bracket, every other row a space there
|
||||
aurm=' '
|
||||
((R_AUR[i] == 0)) || aurm="${C_YELLOW}*${C_RESET}"
|
||||
repo=${R_REPO[i]}
|
||||
[[ $repo != - ]] || repo=
|
||||
flags=
|
||||
[[ -z ${R_FLAGS[i]} ]] || flags=" ${C_YELLOW}[${R_FLAGS[i]}]${C_RESET}"
|
||||
printf -v line '%s%*s %s%-*s%s %-*s -> %s%-*s%s %s%s' "$marker" "$numw" "[${R_NUM[i]}]" \
|
||||
printf -v line '%s%*s%s %s%-*s%s %-*s -> %s%-*s%s %s%s' "$marker" "$numw" "[${R_NUM[i]}]" "$aurm" \
|
||||
"$C_BOLD" "$namew" "${R_NAME[i]}" "$C_RESET" "$oldw" "${R_OLD[i]}" \
|
||||
"$C_GREEN" "$neww" "${R_NEW[i]}" "$C_RESET" "$repo" "$flags"
|
||||
line=${line%"${line##*[! ]}"}
|
||||
@@ -2445,6 +2685,7 @@ session_toggle_all() {
|
||||
}
|
||||
|
||||
SESSION_PROCEED=0
|
||||
UPGRADE_FAILED_MSG= # set by <be>_upgrade when the failing step is not the package manager's own
|
||||
|
||||
# session_line LINE: validate every token first (a typo never executes half a line), then run them
|
||||
session_line() {
|
||||
@@ -2553,6 +2794,8 @@ cmd_interactive() {
|
||||
be upgrade ${excl[@]+"${excl[@]}"} || rc=$?
|
||||
if ((rc == 0)); then
|
||||
if ((opt_dry_run)); then say "Dry run: nothing was changed."; else say "Upgrade finished."; fi
|
||||
elif [[ -n $UPGRADE_FAILED_MSG ]]; then
|
||||
err "$UPGRADE_FAILED_MSG"
|
||||
else
|
||||
err "the upgrade failed: $(be label) exited with status $rc"
|
||||
upgrade_hint "$rc" ${excl[@]+"${excl[@]}"}
|
||||
@@ -2590,6 +2833,9 @@ main() {
|
||||
fi
|
||||
detect_backend
|
||||
ensure_root
|
||||
if [[ $BACKEND != pacman && ($opt_aur == paru || $opt_aur == yay) ]]; then
|
||||
warn "--aur-helper=$opt_aur is ignored: AUR helpers only apply to the pacman backend (Arch Linux and derivatives)"
|
||||
fi
|
||||
recover_leftovers
|
||||
case $cmd in
|
||||
'') cmd_interactive ;;
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
#!/usr/bin/env bats
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# E2E: AUR packages. pacman backend (archlinux): su-dummy-aur 1.0 is a foreign package (fixture
|
||||
# FIXTURE_AUR=1), the fake paru (fixtures/fake-paru.sh) offers it at 2.0 and installs it through
|
||||
# the fake sudo (fixtures/fake-sudo.sh: su, passwordless for wheel). The applet runs either as the
|
||||
# test user su-tester, re-executing itself through that sudo, or as root with SUDO_USER=su-tester
|
||||
# (what the re-exec leaves behind). Rows: [1] su-dummy-a [2]* su-dummy-aur [3] su-dummy-b
|
||||
# [4] su-dummy-c. Other backends: the marker column is a space and no AUR helper ever runs.
|
||||
|
||||
load helpers
|
||||
|
||||
PARU_LOG=/tmp/su-aur-paru.log
|
||||
AUR_OLD=/srv/su-aur/su-dummy-aur-1.0-1-any.pkg.tar.zst
|
||||
|
||||
setup_file() {
|
||||
e2e_setup_file
|
||||
if [[ $BE == pacman ]]; then
|
||||
install -m 0755 "$BATS_TEST_DIRNAME/fixtures/fake-paru.sh" /usr/local/bin/paru
|
||||
install -m 0755 "$BATS_TEST_DIRNAME/fixtures/fake-sudo.sh" /usr/local/bin/sudo
|
||||
else
|
||||
# a paru that only records that it ran (it never may: AUR helpers are for pacman only)
|
||||
printf '#!/bin/sh\necho "$*" >>%s\n' "$PARU_LOG" >/usr/local/bin/paru
|
||||
chmod 0755 /usr/local/bin/paru
|
||||
fi
|
||||
}
|
||||
teardown_file() {
|
||||
rm -f /usr/local/bin/paru "$PARU_LOG"
|
||||
if [[ $BE == pacman ]]; then rm -f /usr/local/bin/sudo; fi
|
||||
}
|
||||
setup() {
|
||||
e2e_reset
|
||||
if [[ $BE == pacman ]]; then
|
||||
pacman -U --noconfirm "$AUR_OLD" >/dev/null 2>&1
|
||||
[[ $(pkg_ver su-dummy-aur) == 1.0 ]]
|
||||
fi
|
||||
: >"$PARU_LOG"
|
||||
chmod 0666 "$PARU_LOG"
|
||||
}
|
||||
|
||||
# as_tester ARGS...: run skeptical-updater as the unprivileged su-tester (its TMPDIR: the suite's
|
||||
# is root's); a run that needs root re-executes itself through the fake sudo
|
||||
as_tester() { runuser -u su-tester -- env TMPDIR=/tmp HOME=/home/su-tester skeptical-updater "$@"; }
|
||||
pacman_only() { [[ $BE == pacman ]] || skip "pacman backend only"; }
|
||||
|
||||
@test "AUR: the row is listed with * and repo aur; paru ran as the invoking user after the sudo re-exec" {
|
||||
pacman_only
|
||||
run as_tester list
|
||||
assert_success
|
||||
assert_line --index 0 "Backend: pacman ($(pretty_name)) + AUR via paru"
|
||||
assert_line 'Refreshing package lists... done.'
|
||||
assert_line '4 upgradable, 0 trusted (hidden, use --all to show), 4 to review:'
|
||||
assert_line ' [1] su-dummy-a 1.0-1 -> 2.0-1 su-dummy'
|
||||
assert_line ' [2]* su-dummy-aur 1.0-1 -> 2.0-1 aur'
|
||||
assert_line ' [3] su-dummy-b 1.0-1 -> 2.0-1 su-dummy'
|
||||
assert_line ' [4] su-dummy-c 1.0-1 -> 2.0-1 su-dummy'
|
||||
run cat "$PARU_LOG"
|
||||
assert_output 'su-tester HOME=/home/su-tester PWD=/home/su-tester -Qua'
|
||||
}
|
||||
|
||||
@test "AUR: N? and N?? on the AUR row (pacman -Qi; merged with paru -Sia)" {
|
||||
pacman_only
|
||||
SUDO_USER=su-tester run skeptical-updater --no-refresh <<<$'2?\n2??\nq'
|
||||
assert_success
|
||||
assert_output --partial "$(printf '%s\n' \
|
||||
'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'
|
||||
run grep -c 'su-tester HOME=/home/su-tester PWD=/home/su-tester -Sia su-dummy-aur' "$PARU_LOG"
|
||||
assert_output 1
|
||||
}
|
||||
|
||||
@test "AUR: N- keeps su-dummy-aur at 1.0 while the repository packages upgrade; paru is not run" {
|
||||
pacman_only
|
||||
SUDO_USER=su-tester run skeptical-updater <<<$'2-\n'
|
||||
assert_success
|
||||
assert_line 'Excluded from this run: su-dummy-aur'
|
||||
assert_line 'Upgrade finished.'
|
||||
[[ $(versions) == "a=2.0 b=2.0 c=2.0" ]] || fail "versions after the run: $(versions)"
|
||||
[[ $(pkg_ver su-dummy-aur) == 1.0 ]] || fail "su-dummy-aur: $(pkg_ver su-dummy-aur)"
|
||||
run grep -c -- '-Sua' "$PARU_LOG"
|
||||
assert_output 0
|
||||
}
|
||||
|
||||
@test "AUR: a plain <enter> upgrades su-dummy-aur to 2.0 with paru, after the repository packages" {
|
||||
pacman_only
|
||||
run as_tester <<<''
|
||||
assert_success
|
||||
assert_line 'Excluded from this run: none'
|
||||
assert_line '+ runuser -u su-tester -- env HOME=/home/su-tester paru -Sua --noconfirm'
|
||||
assert_line 'Upgrade finished.'
|
||||
[[ $(versions) == "a=2.0 b=2.0 c=2.0" ]] || fail "versions after the run: $(versions)"
|
||||
[[ $(pkg_ver su-dummy-aur) == 2.0 ]] || fail "su-dummy-aur: $(pkg_ver su-dummy-aur)"
|
||||
# the repository step came first: su-dummy-a was 2.0 already when paru -Sua started
|
||||
run grep -A1 -- ' -Sua --noconfirm$' "$PARU_LOG"
|
||||
assert_line --index 0 'su-tester HOME=/home/su-tester PWD=/home/su-tester -Sua --noconfirm'
|
||||
assert_line --index 1 ' (at -Sua: su-dummy-a 2.0-1)'
|
||||
run skeptical-updater list --no-refresh --no-aur
|
||||
assert_output --partial 'Nothing to upgrade.'
|
||||
SUDO_USER=su-tester run skeptical-updater list --no-refresh
|
||||
assert_output --partial 'Nothing to upgrade.'
|
||||
}
|
||||
|
||||
@test "AUR: only the AUR row left to upgrade: pacman is not run, only paru" {
|
||||
pacman_only
|
||||
# (the files only: `..` changes with the db.lck of paru's pacman -U)
|
||||
before=$(ls -l --time-style=full-iso /var/lib/pacman/sync)
|
||||
SUDO_USER=su-tester run skeptical-updater <<<$'1- 3- 4-\n'
|
||||
assert_success
|
||||
assert_line 'No repository package to upgrade: pacman is not run, only paru.'
|
||||
assert_line '+ runuser -u su-tester -- env HOME=/home/su-tester paru -Sua --noconfirm --ignore su-dummy-a,su-dummy-b,su-dummy-c'
|
||||
refute_line --regexp '^\+ pacman '
|
||||
[[ $(versions) == "a=1.0 b=1.0 c=1.0" ]] || fail "versions after the run: $(versions)"
|
||||
[[ $(pkg_ver su-dummy-aur) == 2.0 ]] || fail "su-dummy-aur: $(pkg_ver su-dummy-aur)"
|
||||
[[ $(ls -l --time-style=full-iso /var/lib/pacman/sync) == "$before" ]] || fail "the sync database changed although pacman was not run"
|
||||
}
|
||||
|
||||
@test "AUR: --dry-run prints paru's command line (as the invoking user) and changes nothing" {
|
||||
pacman_only
|
||||
SUDO_USER=su-tester run skeptical-updater --dry-run --no-refresh <<<$'3-\n'
|
||||
assert_success
|
||||
assert_line '+ pacman -Su --noconfirm --ignore su-dummy-b'
|
||||
assert_line '+ runuser -u su-tester -- env HOME=/home/su-tester paru -Sua --noconfirm --ignore su-dummy-b'
|
||||
assert_line 'Dry run: nothing was changed.'
|
||||
[[ $(versions) == "a=1.0 b=1.0 c=1.0" && $(pkg_ver su-dummy-aur) == 1.0 ]]
|
||||
run grep -c -- '-Sua' "$PARU_LOG"
|
||||
assert_output 0
|
||||
}
|
||||
|
||||
@test "AUR: unprivileged list --no-refresh runs paru directly, as that user" {
|
||||
pacman_only
|
||||
run as_tester list --no-refresh
|
||||
assert_success
|
||||
assert_line ' [2]* su-dummy-aur 1.0-1 -> 2.0-1 aur'
|
||||
run cat "$PARU_LOG"
|
||||
assert_output --regexp '^su-tester HOME=/home/su-tester PWD=[^ ]+ -Qua$'
|
||||
}
|
||||
|
||||
@test "AUR: a root login (no SUDO_USER): skipped with a warning, the repository rows listed, paru never run" {
|
||||
pacman_only
|
||||
run skeptical-updater list --no-refresh
|
||||
assert_success
|
||||
assert_line --index 0 "Backend: pacman ($(pretty_name))"
|
||||
assert_line --index 1 'skeptical-updater: warning: aur: paru is never run as root and there is no invoking user (SUDO_USER is not set): AUR packages are neither listed nor upgraded (run skeptical-updater through sudo from your own account, or use --no-aur)'
|
||||
assert_line '3 upgradable, 0 trusted (hidden, use --all to show), 3 to review:'
|
||||
refute_output --partial 'su-dummy-aur'
|
||||
[[ ! -s $PARU_LOG ]]
|
||||
}
|
||||
|
||||
@test "AUR: --no-aur lists the repository rows only and never runs paru" {
|
||||
pacman_only
|
||||
run as_tester --no-aur list --no-refresh
|
||||
assert_success
|
||||
assert_line --index 0 "Backend: pacman ($(pretty_name))"
|
||||
assert_line ' [1] su-dummy-a 1.0-1 -> 2.0-1 su-dummy'
|
||||
refute_output --partial 'su-dummy-aur'
|
||||
[[ ! -s $PARU_LOG ]]
|
||||
}
|
||||
|
||||
@test "other backends: the marker column is a space; no AUR helper is ever run, --aur-helper is ignored" {
|
||||
[[ $BE != pacman ]] || skip "not the pacman backend"
|
||||
run skeptical-updater list --no-refresh
|
||||
assert_success
|
||||
assert_line --regexp '^ \[1\] su-dummy-a +1\.0(-1)? +-> +2\.0(-1)? +[^ ]+$'
|
||||
run skeptical-updater --aur-helper=paru list --no-refresh
|
||||
assert_success
|
||||
assert_line "skeptical-updater: warning: --aur-helper=paru is ignored: AUR helpers only apply to the pacman backend (Arch Linux and derivatives)"
|
||||
assert_line --regexp '^ \[3\] su-dummy-c '
|
||||
run skeptical-updater --dry-run --no-refresh <<<''
|
||||
assert_success
|
||||
refute_output --partial 'paru'
|
||||
[[ ! -s $PARU_LOG ]] || fail "paru ran: $(cat "$PARU_LOG")"
|
||||
}
|
||||
@@ -27,8 +27,9 @@ install -m 0755 skeptical-updater /usr/local/bin/skeptical-updater
|
||||
|
||||
if command -v pacman >/dev/null 2>&1; then
|
||||
family=pacman
|
||||
# FIXTURE_SYNC=1: a system whose sync database was synced once (--no-refresh lists from it)
|
||||
FIXTURE_ISOLATE=1 FIXTURE_ORPHAN=1 FIXTURE_SYNC=1 bash tests/e2e/fixtures/pacman.sh
|
||||
# FIXTURE_SYNC=1: a system whose sync database was synced once (--no-refresh lists from it);
|
||||
# FIXTURE_AUR=1: the foreign package su-dummy-aur and the user su-tester for tests/e2e/aur.bats
|
||||
FIXTURE_ISOLATE=1 FIXTURE_ORPHAN=1 FIXTURE_SYNC=1 FIXTURE_AUR=1 bash tests/e2e/fixtures/pacman.sh
|
||||
elif command -v apt-get >/dev/null 2>&1; then
|
||||
family=apt
|
||||
SU_FIXTURE_OFFLINE=1 SU_FIXTURE_ORPHAN=1 SU_FIXTURE_B_AUTO=1 bash tests/e2e/fixtures/apt.sh
|
||||
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# Fake paru for the AUR E2E tests (tests/e2e/aur.bats installs it as /usr/local/bin/paru on the
|
||||
# pacman backend): emulates paru 2.1.0 (discovery/pacman.md section 15) for su-dummy-aur, the
|
||||
# foreign package of the fixture (FIXTURE_AUR=1), whose 2.0 "AUR build" is
|
||||
# /srv/su-aur/su-dummy-aur-2.0-1-any.pkg.tar.zst. No network.
|
||||
# -Qua "su-dummy-aur 1.0-1 -> 2.0-1" (+ " [ignored]" when IgnorePkg names it) while an
|
||||
# older version is installed; otherwise nothing, exit 1
|
||||
# -Sia NAME paru's AUR record of su-dummy-aur 2.0-1 (pacman-style "Key : Value" lines)
|
||||
# -Sua [--noconfirm] [--ignore a,b]
|
||||
# installs the 2.0 package with `sudo pacman -U --noconfirm`, unless su-dummy-aur is
|
||||
# ignored or up to date; refuses to run as root like paru ("can't install AUR package
|
||||
# as root")
|
||||
# Every call is appended to /tmp/su-aur-paru.log as "<user> HOME=<home> PWD=<dir> <args>"; -Sua
|
||||
# also logs the installed su-dummy-a then (the repository step must have upgraded it before).
|
||||
set -uo pipefail
|
||||
LOG=/tmp/su-aur-paru.log
|
||||
PKG=su-dummy-aur NEW=2.0-1 FILE=/srv/su-aur/su-dummy-aur-2.0-1-any.pkg.tar.zst
|
||||
printf '%s HOME=%s PWD=%s %s\n' "$(id -un)" "${HOME-}" "$PWD" "$*" >>"$LOG"
|
||||
|
||||
installed() { pacman -Q "$PKG" 2>/dev/null | cut -d' ' -f2; }
|
||||
ignored_by_conf() { pacman-conf IgnorePkg 2>/dev/null | grep -qx "$PKG"; }
|
||||
|
||||
case ${1:-} in
|
||||
-Qua)
|
||||
v=$(installed)
|
||||
[[ -n $v && $v != "$NEW" ]] || exit 1
|
||||
if ignored_by_conf; then echo "$PKG $v -> $NEW [ignored]"; else echo "$PKG $v -> $NEW"; fi
|
||||
;;
|
||||
-Sia)
|
||||
if [[ ${2:-} != "$PKG" ]]; then
|
||||
echo "error: package '${2:-}' was not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
cat <<REC
|
||||
Repository : aur
|
||||
Name : $PKG
|
||||
Version : $NEW
|
||||
Description : Skeptical-updater dummy package AUR (foreign: in no repository)
|
||||
URL : https://example.invalid/su-dummy
|
||||
AUR URL : https://aur.archlinux.org/packages/$PKG
|
||||
Groups : None
|
||||
Licenses : custom
|
||||
Provides : None
|
||||
Depends On : None
|
||||
Make Deps : None
|
||||
Check Deps : None
|
||||
Optional Deps : None
|
||||
Conflicts With : None
|
||||
Maintainer : su-fixture
|
||||
Votes : 7
|
||||
Popularity : 0.01
|
||||
First Submitted : Tue, 14 Nov 2023 22:13:20
|
||||
Last Modified : Tue, 14 Nov 2023 22:13:20
|
||||
Out Of Date : No
|
||||
|
||||
REC
|
||||
;;
|
||||
-Sua)
|
||||
shift
|
||||
ign=''
|
||||
while (($#)); do
|
||||
case $1 in
|
||||
--ignore)
|
||||
ign=${2:-}
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
echo " (at -Sua: $(pacman -Q su-dummy-a 2>/dev/null))" >>"$LOG"
|
||||
echo ':: Looking for devel upgrades...'
|
||||
if ((EUID == 0)); then
|
||||
echo ':: Resolving dependencies...'
|
||||
echo "error: can't install AUR package as root" >&2
|
||||
exit 1
|
||||
fi
|
||||
v=$(installed)
|
||||
if [[ ,$ign, == *,"$PKG",* ]] || ignored_by_conf; then
|
||||
echo " there is nothing to do"
|
||||
echo "warning: $PKG: ignoring package upgrade ($v => $NEW)" >&2
|
||||
exit 0
|
||||
fi
|
||||
if [[ -z $v || $v == "$NEW" ]]; then
|
||||
echo " there is nothing to do"
|
||||
exit 0
|
||||
fi
|
||||
printf '\nAur (1) Old Version New Version Make Only\naur/%s %s %s No\n\n' "$PKG" "$v" "$NEW"
|
||||
sudo pacman -U --noconfirm "$FILE"
|
||||
;;
|
||||
*)
|
||||
echo "fake paru: unhandled: $*" >&2
|
||||
exit 99
|
||||
;;
|
||||
esac
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# Fake sudo for the AUR E2E tests (tests/e2e/aur.bats installs it as /usr/local/bin/sudo on the
|
||||
# pacman backend): archlinux:latest has no sudo and the E2E run is offline. Runs
|
||||
# "sudo [--] COMMAND..." as root through su, which the fixture (FIXTURE_AUR=1) makes passwordless
|
||||
# for the members of wheel (pam_wheel trust), with the variables sudo sets for the command:
|
||||
# SUDO_USER, SUDO_UID and SUDO_GID. Used for the applet's own re-exec and by the fake paru.
|
||||
[[ ${1:-} != -- ]] || shift
|
||||
if (($# == 0)); then
|
||||
echo "usage: sudo [--] command..." >&2
|
||||
exit 1
|
||||
fi
|
||||
# shellcheck disable=SC2016 # $1... belong to the root shell started by su
|
||||
exec su -s /bin/bash root -c 'exec env SUDO_USER="$1" SUDO_UID="$2" SUDO_GID="$3" "${@:4}"' -- sudo "$(id -un)" "$(id -u)" "$(id -g)" "$@"
|
||||
@@ -3,8 +3,9 @@
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# Deterministic pacman fixture for skeptical-updater E2E tests.
|
||||
# (tests/e2e/fixtures/pacman.sh: copy of discovery/pacman/fixture.sh plus the FIXTURE_ORPHAN
|
||||
# knob; tests/e2e/entrypoint.sh runs it with FIXTURE_ISOLATE=1 FIXTURE_ORPHAN=1.)
|
||||
# (tests/e2e/fixtures/pacman.sh: copy of discovery/pacman/fixture.sh plus the FIXTURE_ORPHAN and
|
||||
# FIXTURE_AUR knobs; tests/e2e/entrypoint.sh runs it with FIXTURE_ISOLATE=1 FIXTURE_ORPHAN=1
|
||||
# FIXTURE_SYNC=1 FIXTURE_AUR=1.)
|
||||
#
|
||||
# Creates three dummy packages installed at 1.0 and a local pacman repo
|
||||
# ([su-dummy], file:///srv/su-repo) that offers them at 2.0:
|
||||
@@ -37,6 +38,11 @@
|
||||
# IgnorePkg) while upgrading su-dummy-a fails with unsatisfied deps.
|
||||
# FIXTURE_ORPHAN 0 (default) | 1 -> also install su-dummy-orphan 1.0 --asdeps with nothing
|
||||
# requiring it (a pre-existing orphan, `pacman -Qdtq` lists it)
|
||||
# FIXTURE_AUR 0 (default) | 1 -> also install su-dummy-aur 1.0, a foreign package (in no
|
||||
# repository: `pacman -Qm` lists it), and keep its 2.0 package file in
|
||||
# /srv/su-aur for the fake paru of tests/e2e/aur.bats; create the user
|
||||
# su-tester (group wheel) and make `su` passwordless for wheel (pam_wheel
|
||||
# trust), which the fake sudo of those tests relies on (the image has no sudo)
|
||||
# FIXTURE_REPO repo directory (default /srv/su-repo)
|
||||
# FIXTURE_SYNC 0 (default) | 1 -> run `pacman -Sy` at the end
|
||||
#
|
||||
@@ -52,6 +58,7 @@ FIXTURE_STRICT_DEP=${FIXTURE_STRICT_DEP:-0}
|
||||
FIXTURE_REPO=${FIXTURE_REPO:-/srv/su-repo}
|
||||
FIXTURE_SYNC=${FIXTURE_SYNC:-0}
|
||||
FIXTURE_ORPHAN=${FIXTURE_ORPHAN:-0}
|
||||
FIXTURE_AUR=${FIXTURE_AUR:-0}
|
||||
FIXTURE_REPO_NAME=su-dummy
|
||||
FIXTURE_EPOCH=1700000000 # fixed build date => reproducible tarballs
|
||||
|
||||
@@ -208,7 +215,24 @@ fx_main() {
|
||||
fi
|
||||
rm -rf "$old"
|
||||
|
||||
if [ "$FIXTURE_AUR" = 1 ]; then
|
||||
mkdir -p /srv/su-aur
|
||||
[ "$FIXTURE_BUILDER" != makepkg ] || chown su-builder: /srv/su-aur
|
||||
fx_build /srv/su-aur su-dummy-aur 1.0 'Skeptical-updater dummy package AUR (foreign: in no repository)'
|
||||
fx_build /srv/su-aur su-dummy-aur 2.0 'Skeptical-updater dummy package AUR (foreign: in no repository)'
|
||||
chmod -R a+rX /srv/su-aur
|
||||
fx_pacman -U --noconfirm /srv/su-aur/su-dummy-aur-1.0-1-any.pkg.tar.zst
|
||||
id su-tester >/dev/null 2>&1 || useradd --create-home --groups wheel su-tester
|
||||
sed -i 's/^#\(auth[[:space:]]\+sufficient[[:space:]]\+pam_wheel\.so trust use_uid\)/\1/' /etc/pam.d/su
|
||||
grep -q '^auth[[:space:]]\+sufficient[[:space:]]\+pam_wheel\.so trust use_uid' /etc/pam.d/su ||
|
||||
fx_die "could not enable pam_wheel trust in /etc/pam.d/su"
|
||||
fi
|
||||
|
||||
if [ "$FIXTURE_SYNC" = 1 ]; then fx_pacman -Sy; fi
|
||||
if [ "$FIXTURE_AUR" = 1 ]; then
|
||||
pacman -Qmq | grep -qx su-dummy-aur || fx_die "su-dummy-aur is not a foreign package"
|
||||
fx_log "foreign (AUR) package: su-dummy-aur 1.0; user su-tester (wheel, passwordless su)"
|
||||
fi
|
||||
|
||||
fx_log "builder=$FIXTURE_BUILDER isolate=$FIXTURE_ISOLATE repo=$FIXTURE_REPO"
|
||||
fx_log "installed: $(pacman -Q su-dummy-a su-dummy-b su-dummy-c 2>/dev/null | tr '\n' ';')"
|
||||
|
||||
@@ -17,7 +17,7 @@ rpm) if command -v zypper >/dev/null 2>&1; then BE=zypper; else BE=dnf; fi ;;
|
||||
*) BE=unknown ;;
|
||||
esac
|
||||
export STATE_DIR STATE_FILE FAMILY BE
|
||||
unset SKEPTICAL_UPDATER_STATE_DIR NO_COLOR
|
||||
unset SKEPTICAL_UPDATER_STATE_DIR NO_COLOR SUDO_USER
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
pretty_name() { (. /etc/os-release && printf '%s' "$PRETTY_NAME"); }
|
||||
|
||||
@@ -0,0 +1,551 @@
|
||||
#!/usr/bin/env bats
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# AUR packages on the pacman backend, through the fake paru/yay (tests/unit/fakes/paru) replaying
|
||||
# the output recorded in discovery/pacman.md section 15: helper detection and overrides, the user
|
||||
# the helper runs as (runuser/sudo -u, SUDO_USER, a root login), the "*" marker column, -Qua
|
||||
# parsing and failures, N? / N?? / N- / N+ / Nx on an AUR row, and the upgrade (repository
|
||||
# packages with pacman first, then `-Sua --ignore ...`; --noconfirm only without a terminal).
|
||||
# Rows with the fake paru: [1]* pfetch [2] su-dummy-a [3]* su-dummy-aur [4] su-dummy-b
|
||||
# [5] su-dummy-c (pfetch and su-dummy-aur are foreign: in no repository).
|
||||
|
||||
# each @test runs in its own subshell: per-test exports are intended
|
||||
# shellcheck disable=SC2030,SC2031
|
||||
|
||||
load test_helper
|
||||
|
||||
setup() {
|
||||
common_setup
|
||||
use_fakes paru pacman runuser getent
|
||||
export SKEPTICAL_UPDATER_OS_RELEASE=$FAKE_DATA/os-release-arch
|
||||
# root through our sudo re-exec, invoked by alice (fake getent: uid 1000, home below)
|
||||
export SUDO_USER=alice
|
||||
ALICE_HOME=$FAKE_DIR/home/alice
|
||||
mkdir -p "$ALICE_HOME"
|
||||
AS_ALICE="runuser -u alice -- env HOME=$ALICE_HOME"
|
||||
}
|
||||
|
||||
ROW1=' [1]* pfetch 1.10.0-1 -> 1.11.0-1 aur'
|
||||
ROW2=' [2] su-dummy-a 1.0-1 -> 2.0-1 su-dummy'
|
||||
ROW3=' [3]* su-dummy-aur 1.0-1 -> 2.0-1 aur'
|
||||
ROW4=' [4] su-dummy-b 1.0-1 -> 2.0-1 su-dummy'
|
||||
ROW5=' [5] su-dummy-c 1.0-1 -> 2.0-1 su-dummy'
|
||||
|
||||
# upgrade_steps: the upgrade's steps from the fake log, in order (throwaway --dbpath normalised)
|
||||
upgrade_steps() {
|
||||
grep -E '^(pacman -Sup --noconfirm|pacman -Su |real sync at -Su|(runuser|sudo) -u .* -Sua|paru -Sua|yay -Sua)' "$FAKE_LOG" |
|
||||
sed -E 's#--dbpath /tmp/skeptical-updater-db\.[^ ]+#--dbpath DB#'
|
||||
}
|
||||
|
||||
# ---- detection and overrides -------------------------------------------------------------------
|
||||
|
||||
@test "detection: paru first; the banner names the helper" {
|
||||
use_fakes paru yay pacman runuser getent
|
||||
run skup list --no-refresh
|
||||
assert_success
|
||||
assert_line --index 0 'Backend: pacman (Arch Linux) + AUR via paru'
|
||||
log_has 'paru -Qua'
|
||||
log_lacks '^yay'
|
||||
}
|
||||
|
||||
@test "detection: yay when paru is not installed" {
|
||||
use_fakes yay pacman runuser getent
|
||||
run skup list --no-refresh
|
||||
assert_success
|
||||
assert_line --index 0 'Backend: pacman (Arch Linux) + AUR via yay'
|
||||
log_has "$AS_ALICE yay -Qua"
|
||||
assert_line "$ROW1"
|
||||
assert_line "$ROW3"
|
||||
}
|
||||
|
||||
@test "pman: the banner reads pman (Arch Linux) + AUR via paru" {
|
||||
use_fakes pman paru pacman runuser getent
|
||||
run skup list --no-refresh
|
||||
assert_success
|
||||
assert_line --index 0 'Backend: pman (Arch Linux) + AUR via paru'
|
||||
}
|
||||
|
||||
@test "--aur-helper=yay picks yay although paru is installed" {
|
||||
use_fakes paru yay pacman runuser getent
|
||||
run skup --aur-helper=yay list --no-refresh
|
||||
assert_success
|
||||
assert_line --index 0 'Backend: pacman (Arch Linux) + AUR via yay'
|
||||
log_has 'yay -Qua'
|
||||
log_lacks '^paru'
|
||||
run skup --aur-helper yay list --no-refresh
|
||||
assert_success
|
||||
assert_line --index 0 'Backend: pacman (Arch Linux) + AUR via yay'
|
||||
}
|
||||
|
||||
@test "--no-aur and --aur-helper=none: no helper runs, no AUR row, plain banner" {
|
||||
for opt in --no-aur --aur-helper=none; do
|
||||
: >"$FAKE_LOG"
|
||||
run skup "$opt" list --no-refresh
|
||||
assert_success
|
||||
assert_line --index 0 'Backend: pacman (Arch Linux)'
|
||||
assert_line '3 upgradable, 0 trusted (hidden, use --all to show), 3 to review:'
|
||||
assert_line ' [1] su-dummy-a 1.0-1 -> 2.0-1 su-dummy'
|
||||
refute_output --partial 'pfetch'
|
||||
log_lacks '^(paru|yay|runuser)'
|
||||
done
|
||||
}
|
||||
|
||||
@test "no helper installed: no AUR, plain banner, every row still has the marker column" {
|
||||
use_fakes pacman runuser getent
|
||||
run skup list --no-refresh
|
||||
assert_success
|
||||
assert_line --index 0 'Backend: pacman (Arch Linux)'
|
||||
assert_line ' [1] su-dummy-a 1.0-1 -> 2.0-1 su-dummy'
|
||||
log_lacks '^runuser'
|
||||
}
|
||||
|
||||
@test "--aur-helper names a helper that is not installed: error (exit 1), nothing runs" {
|
||||
use_fakes paru pacman runuser getent
|
||||
run skup --aur-helper=yay list --no-refresh
|
||||
assert_failure 1
|
||||
assert_output --partial 'error: --aur-helper=yay: yay not found in PATH'
|
||||
[[ -z $(cat "$FAKE_LOG") ]]
|
||||
}
|
||||
|
||||
@test "--aur-helper: an unknown or empty value is a usage error" {
|
||||
run skup --aur-helper=trizen list
|
||||
assert_failure 1
|
||||
assert_output --partial "unknown AUR helper 'trizen' (expected paru, yay or none)"
|
||||
run skup --aur-helper= list
|
||||
assert_failure 1
|
||||
assert_output --partial '--aur-helper needs a value'
|
||||
run skup list --aur-helper
|
||||
assert_failure 1
|
||||
assert_output --partial '--aur-helper needs a value'
|
||||
[[ -z $(cat "$FAKE_LOG") ]]
|
||||
}
|
||||
|
||||
@test "apt: no helper is ever run, even with paru in PATH; --aur-helper is ignored with a warning" {
|
||||
use_fakes apt paru runuser getent
|
||||
export SKEPTICAL_UPDATER_OS_RELEASE=$FAKE_DATA/os-release-debian12
|
||||
run skup list --no-refresh
|
||||
assert_success
|
||||
assert_line --index 0 'Backend: apt (Debian GNU/Linux 12 (bookworm))'
|
||||
assert_line ' [1] su-dummy-a 1.0 -> 2.0 su-fixture:su-repo'
|
||||
refute_output --partial 'aur'
|
||||
run skup --aur-helper=paru list --no-refresh
|
||||
assert_success
|
||||
assert_output --partial 'warning: --aur-helper=paru is ignored: AUR helpers only apply to the pacman backend (Arch Linux and derivatives)'
|
||||
run skup --no-aur list --no-refresh
|
||||
assert_success
|
||||
refute_output --partial 'warning'
|
||||
log_lacks '^(paru|yay|runuser)'
|
||||
}
|
||||
|
||||
# ---- the user the helper runs as ---------------------------------------------------------------
|
||||
|
||||
@test "root through sudo: the helper runs as SUDO_USER with runuser -u, that user's HOME, from that home" {
|
||||
run skup list --no-refresh
|
||||
assert_success
|
||||
log_has "$AS_ALICE paru -Qua"
|
||||
log_has "paru as: HOME=$ALICE_HOME PWD=$ALICE_HOME stdin=notty"
|
||||
}
|
||||
|
||||
@test "root through sudo without runuser: sudo -u USER -- env HOME=..." {
|
||||
use_fakes paru pacman sudo getent
|
||||
run skup list --no-refresh
|
||||
assert_success
|
||||
log_has "sudo -u alice -- env HOME=$ALICE_HOME paru -Qua"
|
||||
log_has "paru as: HOME=$ALICE_HOME PWD=$ALICE_HOME stdin=notty"
|
||||
assert_line "$ROW1"
|
||||
}
|
||||
|
||||
@test "unprivileged (list --no-refresh): the helper runs directly, as we are" {
|
||||
export SKEPTICAL_UPDATER_EUID=1000
|
||||
unset SUDO_USER
|
||||
run skup list --no-refresh
|
||||
assert_success
|
||||
assert_line --index 0 'Backend: pacman (Arch Linux) + AUR via paru'
|
||||
log_has 'paru -Qua'
|
||||
log_lacks '^(runuser|sudo)'
|
||||
assert_line "$ROW1"
|
||||
}
|
||||
|
||||
@test "a root login (no SUDO_USER): AUR skipped with a warning after the banner; repository rows listed" {
|
||||
unset SUDO_USER
|
||||
run skup list --no-refresh
|
||||
assert_success
|
||||
assert_line --index 0 'Backend: pacman (Arch Linux)'
|
||||
assert_line --index 1 'skeptical-updater: warning: aur: paru is never run as root and there is no invoking user (SUDO_USER is not set): AUR packages are neither listed nor upgraded (run skeptical-updater through sudo from your own account, or use --no-aur)'
|
||||
assert_line '3 upgradable, 0 trusted (hidden, use --all to show), 3 to review:'
|
||||
[[ $(grep -c 'warning: aur:' <<<"$output") -eq 1 ]]
|
||||
log_lacks '^(paru|runuser)'
|
||||
}
|
||||
|
||||
@test "SUDO_USER root (or another uid 0 name) or an unknown user: AUR skipped with a warning" {
|
||||
for u in root toor; do
|
||||
SUDO_USER=$u run skup list --no-refresh
|
||||
assert_success
|
||||
assert_line --index 0 'Backend: pacman (Arch Linux)'
|
||||
assert_output --partial "warning: aur: paru is never run as root and the invoking user ($u) is root"
|
||||
done
|
||||
SUDO_USER=nosuchuser run skup list --no-refresh
|
||||
assert_success
|
||||
assert_output --partial "warning: aur: the invoking user 'nosuchuser' (SUDO_USER) is unknown"
|
||||
log_lacks '^(paru|runuser)'
|
||||
}
|
||||
|
||||
@test "a root login: the interactive upgrade runs pacman only, no helper" {
|
||||
unset SUDO_USER
|
||||
run skup --no-refresh <<<''
|
||||
assert_success
|
||||
log_has 'pacman -Su --noconfirm'
|
||||
log_lacks '^(paru|runuser)'
|
||||
}
|
||||
|
||||
# ---- listing ------------------------------------------------------------------------------------
|
||||
|
||||
@test "the * marker: right after the bracket of AUR rows, a space there on the others; columns aligned" {
|
||||
run skup list --no-refresh
|
||||
assert_success
|
||||
assert_line '5 upgradable, 0 trusted (hidden, use --all to show), 5 to review:'
|
||||
assert_output --partial "$(printf '%s\n' "$ROW1" "$ROW2" "$ROW3" "$ROW4" "$ROW5")"
|
||||
}
|
||||
|
||||
@test "the marker column stays aligned with the - and = markers" {
|
||||
write_state "$HEADER" "pacman${T}su-dummy-aur${T}user${T}2026-10-09T09:40:12Z"
|
||||
run skup --all --no-refresh <<<$'1-\nl\nq'
|
||||
assert_success
|
||||
assert_line '-[1]* pfetch 1.10.0-1 -> 1.11.0-1 aur'
|
||||
assert_line '=[3]* su-dummy-aur 1.0-1 -> 2.0-1 aur'
|
||||
assert_line ' [2] su-dummy-a 1.0-1 -> 2.0-1 su-dummy'
|
||||
}
|
||||
|
||||
@test "--no-refresh still lists the AUR rows (the helper always asks the AUR)" {
|
||||
run skup list --no-refresh
|
||||
assert_success
|
||||
log_lacks '^pacman -Sy'
|
||||
log_has "$AS_ALICE paru -Qua"
|
||||
assert_line "$ROW3"
|
||||
}
|
||||
|
||||
@test "with a refresh too: AUR rows next to the rows of the reviewed databases" {
|
||||
run skup list
|
||||
assert_success
|
||||
assert_line 'Refreshing package lists... done.'
|
||||
assert_line "$ROW1"
|
||||
assert_line "$ROW2"
|
||||
}
|
||||
|
||||
@test "-Qua failing (AUR unreachable): the helper's message and a warning; repository rows listed" {
|
||||
FAKE_AUR_QUA=fail run skup list --no-refresh
|
||||
assert_success
|
||||
assert_line --index 0 'Backend: pacman (Arch Linux) + AUR via paru'
|
||||
assert_output --partial 'warning: paru: error: error sending request for url (https://aur.archlinux.org/rpc): error trying to connect'
|
||||
assert_output --partial 'warning: aur: paru -Qua failed, AUR packages not listed'
|
||||
assert_line '3 upgradable, 0 trusted (hidden, use --all to show), 3 to review:'
|
||||
refute_output --partial '*'
|
||||
}
|
||||
|
||||
@test "yay -Qua failing: its multi-line message is shown without the blank lines" {
|
||||
use_fakes yay pacman runuser getent
|
||||
FAKE_AUR_QUA=fail run skup list --no-refresh
|
||||
assert_success
|
||||
assert_output --partial 'warning: yay: -> 1 error occurred:'
|
||||
assert_output --partial 'dial tcp 127.0.0.1:443: connect: connection refused'
|
||||
assert_output --partial 'warning: aur: yay -Qua failed, AUR packages not listed'
|
||||
refute_line 'skeptical-updater: warning: yay: '
|
||||
}
|
||||
|
||||
@test "-Qua with nothing to upgrade (exit 1, silent) is not a failure" {
|
||||
FAKE_AUR_QUA=none run skup list --no-refresh
|
||||
assert_success
|
||||
refute_output --partial 'warning'
|
||||
assert_line '3 upgradable, 0 trusted (hidden, use --all to show), 3 to review:'
|
||||
}
|
||||
|
||||
@test "paru marks IgnorePkg packages [ignored] (shown [held]); yay leaves them out" {
|
||||
FAKE_AUR_QUA=ignored run skup list --no-refresh
|
||||
assert_success
|
||||
assert_line ' [1]* pfetch 1.10.0-1 -> 1.11.0-1 aur [held]'
|
||||
use_fakes yay pacman runuser getent
|
||||
FAKE_AUR_QUA=ignored run skup list --no-refresh
|
||||
assert_success
|
||||
refute_output --partial 'pfetch'
|
||||
assert_line ' [2]* su-dummy-aur 1.0-1 -> 2.0-1 aur'
|
||||
}
|
||||
|
||||
@test "yay: the age suffix ([122d13h]) is no flag; its colored root warning on stdout is no row" {
|
||||
use_fakes yay pacman runuser getent
|
||||
for q in fixture noise; do
|
||||
FAKE_AUR_QUA=$q run skup list --no-refresh
|
||||
assert_success
|
||||
assert_output --partial "$(printf '%s\n' "$ROW1" "$ROW2" "$ROW3")"
|
||||
refute_output --partial 'Avoid'
|
||||
refute_output --partial '122d13h'
|
||||
done
|
||||
}
|
||||
|
||||
@test "devel packages: the new version is latest-commit (paru by default, yay with --devel)" {
|
||||
for h in paru yay; do
|
||||
use_fakes "$h" pacman runuser getent
|
||||
FAKE_AUR_QUA=devel run skup list --no-refresh
|
||||
assert_success
|
||||
assert_line --regexp '^ \[1\]\* pfetch-git +r432\.a906ff8-1 +-> +latest-commit +aur$'
|
||||
done
|
||||
}
|
||||
|
||||
@test "a name a repository lists wins over the AUR row (moved from the AUR into a repository)" {
|
||||
FAKE_AUR_QUA=dup run skup list --no-refresh
|
||||
assert_success
|
||||
assert_line --regexp '^ \[3\] su-dummy-b +1\.0-1 +-> +2\.0-1 +su-dummy$'
|
||||
refute_output --partial '9.0-1'
|
||||
}
|
||||
|
||||
# ---- N? N?? N+ Nx on an AUR row ---------------------------------------------------------------
|
||||
|
||||
@test "N? on an AUR row: description, install reason and dependents from pacman -Qi" {
|
||||
run skup --no-refresh <<<$'1?\nq'
|
||||
assert_success
|
||||
assert_output --partial "$(printf '%s\n' '> 1?' \
|
||||
'pfetch: A pretty system information tool written in POSIX sh.' \
|
||||
'reason: explicitly installed' \
|
||||
'required by: nothing')"
|
||||
log_lacks 'paru -Si'
|
||||
}
|
||||
|
||||
@test "N?? on an AUR row: pacman -Qi merged with paru -Sia (same key column)" {
|
||||
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_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')"
|
||||
}
|
||||
|
||||
@test "N?? with yay: its wider key column is re-padded to pacman's" {
|
||||
use_fakes yay pacman runuser getent
|
||||
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'
|
||||
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#]'
|
||||
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 >" {
|
||||
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'
|
||||
}
|
||||
|
||||
@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')"
|
||||
export SKEPTICAL_UPDATER_EUID=1000
|
||||
unset SUDO_USER
|
||||
: >"$FAKE_LOG"
|
||||
run skup show pfetch
|
||||
assert_success
|
||||
assert_line '> Maintainer : yigits'
|
||||
log_has 'paru -Sia pfetch'
|
||||
log_lacks '^runuser'
|
||||
}
|
||||
|
||||
@test "show of an AUR package as a root login: one warning, the installed record" {
|
||||
unset SUDO_USER
|
||||
run skup show pfetch
|
||||
assert_success
|
||||
assert_line 'Name : pfetch'
|
||||
refute_line --regexp '^> '
|
||||
[[ $(grep -c 'warning: aur:' <<<"$output") -eq 1 ]]
|
||||
log_lacks '^paru'
|
||||
}
|
||||
|
||||
@test "N+ on an AUR row trusts it like any installed package (backend pacman)" {
|
||||
run skup --no-refresh <<<$'1+\nl\nq'
|
||||
assert_success
|
||||
assert_line 'trusted pfetch'
|
||||
assert_line '5 upgradable, 1 trusted (hidden, use --all to show), 4 to review:'
|
||||
run cat "$STATE_FILE"
|
||||
assert_line --regexp "^pacman${T}pfetch${T}user${T}${DATE_RE}\$"
|
||||
}
|
||||
|
||||
@test "Nx on an AUR row purges it with pacman -Rns; the row leaves the list" {
|
||||
run skup --no-refresh <<<$'1x\ny\nl\nq'
|
||||
assert_success
|
||||
log_has 'pacman -Rs --print --print-format %n -- pfetch'
|
||||
assert_line 'Purge pfetch now? [y/N] y'
|
||||
log_has 'pacman -Rns --noconfirm -- pfetch'
|
||||
assert_line '4 upgradable, 0 trusted (hidden, use --all to show), 4 to review:'
|
||||
[[ $(grep -c '^ \[1\]\* pfetch' <<<"$output") -eq 1 ]] || fail "row 1 is still listed after the purge"
|
||||
assert_line ' [3]* su-dummy-aur 1.0-1 -> 2.0-1 aur'
|
||||
}
|
||||
|
||||
# ---- the upgrade --------------------------------------------------------------------------------
|
||||
|
||||
@test "<enter>: the repository step first (pre-check, databases, -Su), then the helper as the invoking user" {
|
||||
run skup <<<''
|
||||
assert_success
|
||||
assert_line 'Upgrade finished.'
|
||||
run upgrade_steps
|
||||
assert_output "$(printf '%s\n' \
|
||||
'pacman -Sup --noconfirm --dbpath DB --print-format %n %v' \
|
||||
'pacman -Su --noconfirm' \
|
||||
'real sync at -Su: reviewed 1 / no-sig' \
|
||||
"$AS_ALICE paru -Sua --noconfirm" \
|
||||
'paru -Sua --noconfirm')"
|
||||
log_has "paru as: HOME=$ALICE_HOME PWD=$ALICE_HOME stdin=notty"
|
||||
}
|
||||
|
||||
@test "N- on an AUR row: the helper's --ignore; pacman never sees AUR names" {
|
||||
run skup --no-refresh <<<$'1-\n'
|
||||
assert_success
|
||||
assert_line 'Excluded from this run: pfetch'
|
||||
log_has 'pacman -Su --noconfirm'
|
||||
assert_line "+ $AS_ALICE paru -Sua --noconfirm --ignore pfetch"
|
||||
log_has 'paru -Sua --noconfirm --ignore pfetch'
|
||||
log_lacks '^pacman .*--ignore'
|
||||
}
|
||||
|
||||
@test "N- on a repository row: pacman --ignore and the helper's --ignore both get it" {
|
||||
run skup --no-refresh <<<$'2-\n1-\n'
|
||||
assert_success
|
||||
log_has 'pacman -Su --noconfirm --ignore su-dummy-a'
|
||||
log_has "$AS_ALICE paru -Sua --noconfirm --ignore pfetch,su-dummy-a"
|
||||
}
|
||||
|
||||
@test "every AUR row excluded: the helper is not run" {
|
||||
run skup --no-refresh <<<$'1-\n3-\n'
|
||||
assert_success
|
||||
log_has 'pacman -Su --noconfirm'
|
||||
log_lacks '-Sua'
|
||||
}
|
||||
|
||||
@test "only AUR rows left to upgrade: pacman is not run, only the helper" {
|
||||
run skup <<<$'2- 4- 5-\n'
|
||||
assert_success
|
||||
assert_line 'No repository package to upgrade: pacman is not run, only paru.'
|
||||
log_lacks '^pacman -Su'
|
||||
log_lacks '^pacman -Sup --noconfirm'
|
||||
log_lacks '^real sync'
|
||||
log_has "$AS_ALICE paru -Sua --noconfirm --ignore su-dummy-a,su-dummy-b,su-dummy-c"
|
||||
assert_line 'Upgrade finished.'
|
||||
}
|
||||
|
||||
@test "no AUR row at all: exactly the repository upgrade as before" {
|
||||
FAKE_AUR_QUA=none run skup --no-refresh <<<''
|
||||
assert_success
|
||||
log_has 'pacman -Su --noconfirm'
|
||||
log_lacks '-Sua'
|
||||
}
|
||||
|
||||
@test "trusted (hidden) AUR rows are upgraded by the helper too" {
|
||||
write_state "$HEADER" "pacman${T}pfetch${T}user${T}2026-10-09T09:40:12Z" "pacman${T}su-dummy-aur${T}user${T}2026-10-09T09:40:12Z"
|
||||
# listed: [1] su-dummy-a [2] su-dummy-b [3] su-dummy-c (both AUR rows trusted: hidden)
|
||||
run skup --no-refresh <<<$'1- 2- 3-\n'
|
||||
assert_success
|
||||
assert_line 'No repository package to upgrade: pacman is not run, only paru.'
|
||||
log_has "$AS_ALICE paru -Sua --noconfirm --ignore su-dummy-a,su-dummy-b,su-dummy-c"
|
||||
}
|
||||
|
||||
@test "an [ignored] AUR row alone does not make pacman stand aside" {
|
||||
FAKE_AUR_QUA=ignored run skup --no-refresh <<<$'2- 3- 4- 5-\n'
|
||||
assert_success
|
||||
log_has 'pacman -Su --noconfirm --ignore su-dummy-a,su-dummy-b,su-dummy-c'
|
||||
log_lacks '-Sua'
|
||||
}
|
||||
|
||||
@test "the helper failing after a successful repository step: reported, its exit status returned" {
|
||||
FAKE_AUR_SUA_RC=1 run skup --no-refresh <<<''
|
||||
assert_failure 1
|
||||
log_has 'pacman -Su --noconfirm'
|
||||
assert_output --partial 'error: the AUR upgrade failed: paru exited with status 1 (the repository packages were upgraded)'
|
||||
refute_output --partial 'hint:'
|
||||
refute_output --partial 'Upgrade finished.'
|
||||
}
|
||||
|
||||
@test "the helper failing in an AUR-only upgrade: its exit status" {
|
||||
FAKE_AUR_SUA_RC=4 run skup --no-refresh <<<$'2- 4- 5-\n'
|
||||
assert_failure 4
|
||||
assert_output --partial 'error: the AUR upgrade failed: paru exited with status 4'
|
||||
refute_output --partial 'repository packages were upgraded'
|
||||
}
|
||||
|
||||
@test "a failing repository step: the helper is not run" {
|
||||
FAKE_PACMAN_SU_RC=1 run skup --no-refresh <<<''
|
||||
assert_failure 1
|
||||
assert_output --partial 'the upgrade failed: pacman exited with status 1'
|
||||
log_lacks '-Sua'
|
||||
FAKE_PACMAN_SUP_RC=1 run skup <<<'2-'$'\n'
|
||||
assert_failure 1
|
||||
log_lacks '-Sua'
|
||||
}
|
||||
|
||||
@test "--dry-run prints the helper's command line (root through sudo: as the invoking user)" {
|
||||
run skup --dry-run --no-refresh <<<$'1-\n'
|
||||
assert_success
|
||||
assert_line '+ pacman -Su --noconfirm'
|
||||
assert_line "+ $AS_ALICE paru -Sua --noconfirm --ignore pfetch"
|
||||
assert_line 'Dry run: nothing was changed.'
|
||||
log_lacks '-Sua'
|
||||
log_lacks '^pacman -Su '
|
||||
}
|
||||
|
||||
@test "--dry-run unprivileged: the helper's plain command line" {
|
||||
export SKEPTICAL_UPDATER_EUID=1000
|
||||
unset SUDO_USER
|
||||
run skup --dry-run <<<''
|
||||
assert_success
|
||||
assert_line '+ paru -Sua --noconfirm'
|
||||
log_lacks '-Sua'
|
||||
log_lacks '^(runuser|sudo)'
|
||||
}
|
||||
|
||||
@test "on a terminal: no --noconfirm, the helper gets the terminal (its PKGBUILD review)" {
|
||||
pty_skup $'3-\r\r' --no-refresh
|
||||
assert_success
|
||||
log_has "$AS_ALICE paru -Sua --ignore su-dummy-aur"
|
||||
log_has "paru as: HOME=$ALICE_HOME PWD=$ALICE_HOME stdin=tty"
|
||||
log_lacks 'noconfirm --ignore su-dummy-aur'
|
||||
log_has 'pacman -Su --noconfirm'
|
||||
}
|
||||
|
||||
@test "help and --help mention the * marker and the options" {
|
||||
run skup --no-refresh <<<$'h\nq'
|
||||
assert_success
|
||||
assert_line " l redraw the list (a '*' after the number marks an AUR package)"
|
||||
run skup --help
|
||||
assert_success
|
||||
assert_line --partial '--aur-helper=NAME pacman backend: list and upgrade AUR packages with paru or yay, or'
|
||||
assert_line --partial '--no-aur same as --aur-helper=none'
|
||||
assert_output --partial "A '*' right after the number marks an AUR package"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
error: error sending request for url (https://aur.archlinux.org/rpc): error trying to connect: tcp connect error: Connection refused (os error 111): error trying to connect: tcp connect error: Connection refused (os error 111): tcp connect error: Connection refused (os error 111): Connection refused (os error 111)
|
||||
@@ -0,0 +1,4 @@
|
||||
-> 1 error occurred:
|
||||
* request failed: Get "https://aur.archlinux.org/rpc?arg%5B%5D=paru&arg%5B%5D=pfetch&arg%5B%5D=su-dummy-aur&type=info&v=5": dial tcp 127.0.0.1:443: connect: connection refused
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pfetch-git r432.a906ff8-1 -> latest-commit
|
||||
@@ -0,0 +1,2 @@
|
||||
pfetch 1.10.0-1 -> 1.11.0-1
|
||||
su-dummy-b 1.0-1 -> 9.0-1
|
||||
@@ -0,0 +1,2 @@
|
||||
pfetch 1.10.0-1 -> 1.11.0-1
|
||||
su-dummy-aur 1.0-1 -> 2.0-1
|
||||
@@ -0,0 +1,2 @@
|
||||
pfetch 1.10.0-1 -> 1.11.0-1 [ignored]
|
||||
su-dummy-aur 1.0-1 -> 2.0-1
|
||||
@@ -0,0 +1,2 @@
|
||||
pfetch 1.10.0-1 -> 1.11.0-1
|
||||
su-dummy-aur 1.0-1 -> 2.0-1
|
||||
@@ -0,0 +1 @@
|
||||
pfetch-git r432.a906ff8-1 -> latest-commit [2299d6h]
|
||||
@@ -0,0 +1,2 @@
|
||||
pfetch 1.10.0-1 -> 1.11.0-1 [122d13h]
|
||||
su-dummy-b 1.0-1 -> 9.0-1 [1d2h]
|
||||
@@ -0,0 +1,2 @@
|
||||
pfetch 1.10.0-1 -> 1.11.0-1 [122d13h]
|
||||
su-dummy-aur 1.0-1 -> 2.0-1 [3d4h]
|
||||
@@ -0,0 +1 @@
|
||||
su-dummy-aur 1.0-1 -> 2.0-1 [3d4h]
|
||||
@@ -0,0 +1,3 @@
|
||||
[1m[33m -> [0m[0mAvoid running yay as root/sudo.
|
||||
pfetch 1.10.0-1 -> 1.11.0-1 [122d13h]
|
||||
su-dummy-aur 1.0-1 -> 2.0-1 [3d4h]
|
||||
@@ -0,0 +1,21 @@
|
||||
Repository : aur
|
||||
Name : pfetch
|
||||
Version : 1.11.0-1
|
||||
Description : A pretty system information tool written in POSIX sh.
|
||||
URL : https://github.com/Un1q32/pfetch
|
||||
AUR URL : https://aur.archlinux.org/packages/pfetch
|
||||
Groups : None
|
||||
Licenses : MIT
|
||||
Provides : pfetch
|
||||
Depends On : None
|
||||
Make Deps : None
|
||||
Check Deps : None
|
||||
Optional Deps : None
|
||||
Conflicts With : pfetch-git
|
||||
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
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
Repository : aur
|
||||
Name : pfetch
|
||||
Version : 1.11.0-1
|
||||
Description : A pretty system information tool written in POSIX sh.
|
||||
URL : https://github.com/Un1q32/pfetch
|
||||
Licenses : MIT
|
||||
Groups : None
|
||||
Provides : pfetch
|
||||
Depends On : None
|
||||
Optional Deps : None
|
||||
Make Deps : None
|
||||
Check Deps : None
|
||||
Conflicts With : pfetch-git
|
||||
Replaces : None
|
||||
AUR URL : https://aur.archlinux.org/packages/pfetch
|
||||
First Submitted : Sat 16 Jan 2021 09:13:08 AM UTC
|
||||
Keywords : fetch
|
||||
Last Modified : Tue 09 Jun 2026 10:43:50 PM UTC
|
||||
Maintainer : yigits
|
||||
Popularity : 0.103440
|
||||
Votes : 48
|
||||
Out-of-date : No
|
||||
|
||||
@@ -288,3 +288,47 @@ Install Date : Sun Oct 4 00:00:00 2026
|
||||
Install Reason : Installed as a dependency for another package
|
||||
Install Script : No
|
||||
Validated By : Signature
|
||||
|
||||
Name : pfetch
|
||||
Version : 1.10.0-1
|
||||
Description : A pretty system information tool written in POSIX sh.
|
||||
Architecture : any
|
||||
URL : https://github.com/Un1q32/pfetch
|
||||
Licenses : MIT
|
||||
Groups : None
|
||||
Provides : pfetch
|
||||
Depends On : None
|
||||
Optional Deps : None
|
||||
Required By : None
|
||||
Optional For : None
|
||||
Conflicts With : pfetch-git
|
||||
Replaces : None
|
||||
Installed Size : 81.50 KiB
|
||||
Packager : Unknown Packager
|
||||
Build Date : Sat Oct 10 12:42:02 2026
|
||||
Install Date : Sat Oct 10 12:42:04 2026
|
||||
Install Reason : Explicitly installed
|
||||
Install Script : No
|
||||
Validated By : None
|
||||
|
||||
Name : su-dummy-aur
|
||||
Version : 1.0-1
|
||||
Description : Skeptical-updater dummy package AUR (foreign: in no repository)
|
||||
Architecture : any
|
||||
URL : https://example.invalid/su-dummy
|
||||
Licenses : custom
|
||||
Groups : None
|
||||
Provides : None
|
||||
Depends On : None
|
||||
Optional Deps : None
|
||||
Required By : None
|
||||
Optional For : None
|
||||
Conflicts With : None
|
||||
Replaces : None
|
||||
Installed Size : 4.00 B
|
||||
Packager : su-fixture <su-fixture@example.invalid>
|
||||
Build Date : Tue Nov 14 22:13:20 2023
|
||||
Install Date : Fri Oct 9 08:00:00 2026
|
||||
Install Reason : Explicitly installed
|
||||
Install Script : No
|
||||
Validated By : None
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake getent (unit tests): "getent passwd NAME" for alice (uid 1000, home $FAKE_DIR/home/alice),
|
||||
# root and toor (uid 0); exit 2 (no such key) for anyone else
|
||||
[[ ${1:-} == passwd ]] || exit 1
|
||||
case ${2:-} in
|
||||
alice) echo "alice:x:1000:1000:Alice:${FAKE_DIR:?}/home/alice:/bin/bash" ;;
|
||||
root) echo "root:x:0:0::/root:/bin/bash" ;;
|
||||
toor) echo "toor:x:0:0:root by another name:/root:/bin/bash" ;;
|
||||
*) exit 2 ;;
|
||||
esac
|
||||
@@ -145,7 +145,7 @@ case $op in
|
||||
fi
|
||||
case $tg in
|
||||
su-dummy-a) printf '%s\n' su-dummy-a su-dummy-b ;; # b was installed as a dependency of a
|
||||
su-dummy-c | glib2 | expat | ca-certificates-mozilla) echo "$tg" ;;
|
||||
su-dummy-c | glib2 | expat | ca-certificates-mozilla | pfetch | su-dummy-aur) echo "$tg" ;; # (the last two: foreign, AUR)
|
||||
*)
|
||||
echo "error: target not found: $tg" >&2
|
||||
exit 1
|
||||
|
||||
Executable
+58
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake paru (unit tests; fakes/yay/yay runs this file as yay): replays the output of paru 2.1.0
|
||||
# and yay 13.0.1 recorded in discovery/pacman.md section 15 (tests/unit/data/aur/) and logs every
|
||||
# call, followed by "<helper> as: HOME=... PWD=... stdin=tty|notty" (where and how it runs).
|
||||
# FAKE_AUR_QUA selects what -Qua prints: fixture (default: pfetch and su-dummy-aur), ignored
|
||||
# (pfetch in IgnorePkg: paru marks it [ignored], yay leaves it out), devel (a -git package whose
|
||||
# upstream moved: "-> latest-commit"), dup (an AUR row for su-dummy-b, which a repository has),
|
||||
# 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-<helper>-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).
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
fake_log "$@"
|
||||
if [[ -t 0 ]]; then stdin='tty'; else stdin='notty'; fi
|
||||
printf '%s as: HOME=%s PWD=%s stdin=%s\n' "$FAKE_NAME" "${HOME-}" "$PWD" "$stdin" >>"$FAKE_LOG"
|
||||
D=$FAKE_DATA/aur
|
||||
|
||||
case ${1:-} in
|
||||
-Qua)
|
||||
case ${FAKE_AUR_QUA:-fixture} in
|
||||
none) exit 1 ;;
|
||||
fail)
|
||||
cat "$D/Qua-fail-$FAKE_NAME.err" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
out=$(
|
||||
while IFS= read -r line; do
|
||||
fake_purged "${line%% *}" || printf '%s\n' "$line"
|
||||
done <"$D/Qua-$FAKE_NAME-${FAKE_AUR_QUA:-fixture}.txt"
|
||||
)
|
||||
[[ -n $out ]] || exit 1
|
||||
printf '%s\n' "$out"
|
||||
;;
|
||||
-Sia)
|
||||
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"
|
||||
exit 0
|
||||
fi
|
||||
if [[ $FAKE_NAME == yay ]]; then
|
||||
echo " -> No AUR package found for ${2:-}" >&2
|
||||
else
|
||||
echo "error: package '${2:-}' was not found" >&2
|
||||
fi
|
||||
exit 1
|
||||
;;
|
||||
-Sua) exit "$(fake_rc FAKE_AUR_SUA_RC 0)" ;;
|
||||
*)
|
||||
echo "fake $FAKE_NAME: unhandled: $*" >&2
|
||||
exit 99
|
||||
;;
|
||||
esac
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake runuser (unit tests): logs "runuser -u USER -- CMD..." and runs CMD (as the same user)
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
fake_log "$@"
|
||||
if [[ ${1:-} != -u || ${3:-} != -- ]]; then
|
||||
echo "fake runuser: unexpected arguments: $*" >&2
|
||||
exit 98
|
||||
fi
|
||||
shift 3
|
||||
exec "$@"
|
||||
@@ -2,8 +2,13 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake sudo (unit tests): logs the command it was asked to run, runs nothing
|
||||
# fake sudo (unit tests): logs the command it was asked to run; runs nothing (the re-exec of the
|
||||
# applet), except "sudo -u USER -- CMD...", which runs CMD (as the same user: an AUR helper)
|
||||
# shellcheck source=../fakelib.bash
|
||||
. "${BASH_SOURCE[0]%/*}/../fakelib.bash"
|
||||
fake_log "$@"
|
||||
if [[ ${1:-} == -u && ${3:-} == -- ]]; then
|
||||
shift 3
|
||||
exec "$@"
|
||||
fi
|
||||
exit 0
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
# fake yay (unit tests): the fake paru, replaying yay's recorded output instead
|
||||
FAKE_AS=yay exec "${BASH_SOURCE[0]%/*}/../paru/paru" "$@"
|
||||
@@ -368,7 +368,7 @@ EOF
|
||||
assert_line --regexp '^ \[5\] su-dummy-c +1\.0-1 +-> +2\.0-1 +su-dummy$'
|
||||
# epoch kept on both sides
|
||||
assert_line --regexp '^ \[6\] vim-minimal +2:8\.2\.2637-26\.el9_8\.21 +-> +2:8\.2\.2637-26\.el9_8\.23 +baseos$'
|
||||
refute_line --regexp '\] su-dummy-new'
|
||||
refute_line --regexp '\] +su-dummy-new'
|
||||
refute_output --partial 'su-dummy-old'
|
||||
refute_output --partial 'Obsoleting'
|
||||
assert_line 'Note: the upgrade also installs (obsoleting installed packages): su-dummy-new'
|
||||
|
||||
@@ -36,7 +36,7 @@ common_setup() {
|
||||
export TMPDIR=$BATS_TEST_TMPDIR
|
||||
export FAKE_PACMAN_DBPATH=$FAKE_DIR/pacman-db
|
||||
mkdir -p "$FAKE_PACMAN_DBPATH/sync" "$FAKE_PACMAN_DBPATH/local"
|
||||
unset NO_COLOR ZYPP_CONF COLUMNS SKEPTICAL_UPDATER_LOCK_WAIT
|
||||
unset NO_COLOR ZYPP_CONF COLUMNS SKEPTICAL_UPDATER_LOCK_WAIT SUDO_USER
|
||||
use_fakes apt
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user