aboutsummaryrefslogtreecommitdiff
path: root/scripts/audit-packages.sh
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/audit-packages.sh')
-rwxr-xr-xscripts/audit-packages.sh135
1 files changed, 135 insertions, 0 deletions
diff --git a/scripts/audit-packages.sh b/scripts/audit-packages.sh
new file mode 100755
index 0000000..f7af19f
--- /dev/null
+++ b/scripts/audit-packages.sh
@@ -0,0 +1,135 @@
+#!/bin/bash
+# audit-packages.sh — verify every package archsetup installs still exists
+# at its declared source, and flag packages that moved between the official
+# repos and the AUR.
+#
+# Extraction covers direct `pacman_install pkg` / `aur_install pkg` calls
+# and `for software in a b c; do` loop lists (backslash continuations
+# included). Variable arguments ("$software") are the loop plumbing, not
+# packages, and are skipped.
+#
+# Checks:
+# official package missing from sync dbs -> MOVED TO AUR (if AUR has it)
+# or MISSING EVERYWHERE
+# AUR package gone from the AUR -> NOW IN OFFICIAL (if repos
+# have it) or MISSING EVERYWHERE
+#
+# Usage: audit-packages.sh [--list] [installer-script]
+# --list print the extracted package lists and exit (no network)
+# default audit; exit 1 when anything is missing or moved
+#
+# Env overrides (tests): AUDIT_PACMAN, AUDIT_CURL.
+
+set -u
+
+PACMAN="${AUDIT_PACMAN:-pacman}"
+CURL="${AUDIT_CURL:-curl}"
+
+mode=audit
+case "${1:-}" in
+ --list) mode=list; shift ;;
+esac
+
+script="${1:-$(dirname "$0")/../archsetup}"
+if [ ! -r "$script" ]; then
+ echo "audit-packages: cannot read installer script: $script" >&2
+ exit 2
+fi
+
+# Extract package names per source. Loop lists are attributed to the
+# install command used inside the loop body.
+extract() {
+ awk '
+ # Collect a `for software in ...` list (may span backslash-continued
+ # lines); attribute it when the loop body shows which installer runs.
+ /for[ \t]+[A-Za-z_]+[ \t]+in[ \t]/ {
+ line = $0
+ sub(/^.*[ \t]in[ \t]+/, "", line)
+ listpkgs = ""
+ while (1) {
+ stop = (line ~ /;[ \t]*do([ \t]|$)/)
+ sub(/;[ \t]*do.*$/, "", line)
+ gsub(/\\/, "", line)
+ listpkgs = listpkgs " " line
+ if (stop) break
+ if ((getline line) <= 0) break
+ }
+ pending = listpkgs
+ next
+ }
+ /^[ \t]*(pacman_install|aur_install)[ \t]/ {
+ cmd = ($0 ~ /pacman_install/) ? "official" : "aur"
+ arg = $2
+ gsub(/["'\'']/, "", arg)
+ if (arg ~ /^\$/) {
+ # loop plumbing — emit the pending for-list under this source
+ n = split(pending, w, /[ \t]+/)
+ for (i = 1; i <= n; i++) if (w[i] != "") print cmd, w[i]
+ pending = ""
+ } else if (arg != "") {
+ print cmd, arg
+ }
+ }
+ ' "$script" | sort -u
+}
+
+pairs=$(extract)
+official=$(echo "$pairs" | awk '$1 == "official" { print $2 }')
+aur=$(echo "$pairs" | awk '$1 == "aur" { print $2 }')
+
+if [ "$mode" = list ]; then
+ echo "OFFICIAL ($(echo "$official" | grep -c .)):"
+ echo "$official" | sed 's/^/ /'
+ echo "AUR ($(echo "$aur" | grep -c .)):"
+ echo "$aur" | sed 's/^/ /'
+ exit 0
+fi
+
+# One batched AUR RPC query answers existence for every AUR-relevant name.
+aur_query() {
+ # args: package names; prints the names the AUR knows
+ local args="" p
+ for p in "$@"; do args="$args&arg[]=$p"; done
+ "$CURL" -sm 20 "https://aur.archlinux.org/rpc/v5/info?${args#&}" 2>/dev/null \
+ | tr ',' '\n' | sed -n 's/.*"Name":[[:space:]]*"\([^"]*\)".*/\1/p'
+}
+
+# shellcheck disable=SC2086
+aur_known=$(aur_query $official $aur)
+
+in_repos() { "$PACMAN" -Si "$1" >/dev/null 2>&1; }
+in_aur() { echo "$aur_known" | grep -qx "$1"; }
+
+moved_to_aur="" now_official="" missing=""
+for p in $official; do
+ in_repos "$p" && continue
+ if in_aur "$p"; then moved_to_aur="$moved_to_aur $p"; else missing="$missing $p"; fi
+done
+for p in $aur; do
+ if in_aur "$p"; then
+ in_repos "$p" && now_official="$now_official $p (also in repos)"
+ continue
+ fi
+ if in_repos "$p"; then now_official="$now_official $p"; else missing="$missing $p"; fi
+done
+
+rc=0
+if [ -n "$missing" ]; then
+ echo "MISSING EVERYWHERE (not in repos, not in AUR):"
+ for p in $missing; do echo " $p"; done
+ rc=1
+fi
+if [ -n "$moved_to_aur" ]; then
+ echo "MOVED TO AUR (pacman_install will fail; switch to aur_install):"
+ for p in $moved_to_aur; do echo " $p"; done
+ rc=1
+fi
+if [ -n "$now_official" ]; then
+ echo "NOW IN OFFICIAL (aur_install works but pacman_install is cleaner):"
+ echo "$now_official" | tr ' ' '\n' | grep -v '^$' | sed 's/^/ /'
+ rc=1
+fi
+total=$(( $(echo "$official" | grep -c .) + $(echo "$aur" | grep -c .) ))
+echo "---"
+echo "$total packages audited"
+exit $rc