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

+97
View File
@@ -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
+16
View File
@@ -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)" "$@"
+26 -2
View File
@@ -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' ';')"