#!/usr/bin/env bash # Daily AUR rebuild for the [tanin] binary repo. # # aur-autoupdate.sh setup # one-time: install devtools + create the chroot # aur-autoupdate.sh run # the daily job (driven by the systemd user timer) # aur-autoupdate.sh status # show what each package would do, without building # # What it does on `run`: # * fetch each AUR package's PKGBUILD repo into a cache, # * build it in a clean chroot (devtools) — no build deps leak into the host, # no interactive sudo (see the shipped sudoers drop-in), # * but skip non -git packages whose AUR version is unchanged (calamares etc. # don't get rebuilt every day for nothing); -git packages always rebuild, # * repo-add the fresh packages into tanin.db, prune superseded files, # * if TANIN_PUBLISH_CMD is set, run it to upload (hosting is not wired yet, # so by default this only refreshes the LOCAL repo dir). # # Config (env overrides): # TANIN_REPO_DIR output repo dir (default ~/projects/tanin-repo) # TANIN_CHROOT clean chroot location (default ~/.cache/tanin-chroot) # TANIN_AUR_CACHE AUR clone + state cache (default ~/.cache/tanin-aur) # TANIN_PUBLISH_CMD upload hook, e.g. an rsync (default empty = local only) set -uo pipefail REPO_NAME="tanin" OUT="${TANIN_REPO_DIR:-$HOME/projects/tanin-repo}" DB="$OUT/$REPO_NAME.db.tar.zst" CHROOT="${TANIN_CHROOT:-$HOME/.cache/tanin-chroot}" CACHE="${TANIN_AUR_CACHE:-$HOME/.cache/tanin-aur}" PUBLISH="${TANIN_PUBLISH_CMD:-}" # AUR-only deps of TANINUX (not in the official repos). Mirrors the list in # finish-tanin-repo.sh — keep them in sync. AUR_PKGS=(eww-git tiramisu-git waypaper calamares librewolf-bin arch-update timeshift-autosnap xdg-terminal-exec paru) log() { printf '[%s] %s\n' "$REPO_NAME-aur" "$*"; } # --- one-time setup -------------------------------------------------------- setup() { sudo pacman -S --needed --noconfirm devtools git jq curl mkdir -p "$CHROOT" "$CACHE" "$OUT" if [ ! -d "$CHROOT/root" ]; then log "creating clean chroot at $CHROOT (base-devel) …" mkarchroot "$CHROOT/root" base-devel else log "chroot already present at $CHROOT" fi log "setup done." } # latest AUR version (ver-rel, may include epoch) via the RPC; empty on failure. aur_version() { curl -fsSL "https://aur.archlinux.org/rpc/v5/info?arg%5B%5D=$1" 2>/dev/null \ | jq -r '.results[0].Version // empty' 2>/dev/null } # best-effort version of the newest built package file in a dir (ver-rel). built_version() { local f; f="$(ls -t "$1"/*.pkg.tar.zst 2>/dev/null | head -1)" || return 1 [ -n "$f" ] || return 1 basename "$f" | sed -E 's/.*-([^-]+-[0-9]+)-[^-]+\.pkg\.tar\.zst$/\1/' } # decide whether $1 needs a rebuild. -git: always. others: AUR ver != last built. should_build() { local p="$1" [[ "$p" == *-git ]] && return 0 local rpc; rpc="$(aur_version "$p")" [ -z "$rpc" ] && return 0 # RPC down → build to be safe local last; last="$(cat "$CACHE/$p.ver" 2>/dev/null || echo)" [ "$rpc" != "$last" ] } # fetch/refresh the AUR PKGBUILD repo for $1 into the cache. fetch() { local p="$1" dir="$CACHE/$p" if [ -d "$dir/.git" ]; then git -C "$dir" pull -q --ff-only 2>/dev/null || { rm -rf "$dir"; } fi [ -d "$dir/.git" ] || git clone -q "https://aur.archlinux.org/$p.git" "$dir" } # build $1 in the clean chroot and copy results into the repo dir. build_one() { local p="$1" dir="$CACHE/$p" fetch "$p" || { log "FETCH FAILED: $p"; return 1; } if ! should_build "$p"; then log "up to date: $p" return 2 fi log "building: $p" rm -f "$dir"/*.pkg.tar.zst # -c = clean copy of the chroot each time; -r = which chroot; -- = makepkg args if ( cd "$dir" && makechrootpkg -c -r "$CHROOT" -- --noconfirm ); then cp "$dir"/*.pkg.tar.zst "$OUT"/ && log " ok: $p" built_version "$dir" > "$CACHE/$p.ver" 2>/dev/null || true return 0 fi log " BUILD FAILED: $p" return 1 } run() { [ -d "$CHROOT/root" ] || { log "no chroot — run '$0 setup' first"; exit 1; } mkdir -p "$OUT" "$CACHE" local built=0 failed=0 for p in "${AUR_PKGS[@]}"; do build_one "$p"; case $? in 0) built=$((built+1));; 1) failed=$((failed+1));; esac done if [ "$built" -gt 0 ]; then log "refreshing DB ($built rebuilt)" repo-add -q "$DB" "$OUT"/*.pkg.tar.zst >/dev/null # keep only the newest file per package on disk command -v paccache >/dev/null && paccache -rq -k1 -c "$OUT" >/dev/null 2>&1 || true if [ -n "$PUBLISH" ]; then log "publishing via TANIN_PUBLISH_CMD" eval "$PUBLISH" || log "PUBLISH FAILED" else log "no TANIN_PUBLISH_CMD set — local repo only (hosting not wired yet)" fi else log "nothing rebuilt — DB untouched" fi log "done (rebuilt=$built failed=$failed)" [ "$failed" -eq 0 ] } status() { for p in "${AUR_PKGS[@]}"; do if [[ "$p" == *-git ]]; then printf '%-22s -git → always rebuild\n' "$p" else local rpc last; rpc="$(aur_version "$p")"; last="$(cat "$CACHE/$p.ver" 2>/dev/null || echo -)" printf '%-22s aur=%-16s built=%-16s %s\n' "$p" "${rpc:-?}" "$last" \ "$([ "$rpc" != "$last" ] && echo '→ REBUILD' || echo 'up-to-date')" fi done } case "${1:-run}" in setup) setup ;; run) run ;; status) status ;; *) echo "usage: $0 {setup|run|status}" >&2; exit 2 ;; esac