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:
bryanpedini committed 2026-10-10 15:06:18 +02:00
1 parent 875c8294bf
commit def537af58
39 files changed
+1903 -115

No files matched your search

+126
View File
@@ -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>]` |