231 lines
6.2 KiB
Bash
Executable File
231 lines
6.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
shopt -s nullglob
|
|
|
|
repoDir="/var/www/packages.stormux.org"
|
|
aurRpcUrl="https://aur.archlinux.org/rpc/v5/info"
|
|
exclude=("gzdoom")
|
|
|
|
require_cmd() {
|
|
local cmd="$1"
|
|
|
|
if ! command -v "$cmd" >/dev/null 2>&1; then
|
|
printf 'Required command not found: %s\n' "$cmd" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
is_excluded() {
|
|
local packageName="$1"
|
|
local excludedPackage
|
|
|
|
for excludedPackage in "${exclude[@]}"; do
|
|
if [[ "$excludedPackage" == "$packageName" ]]; then
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
record_local_package_version() {
|
|
local packageName="$1"
|
|
local packageVersion="$2"
|
|
local packageMapName="$3"
|
|
local -n packageMapRef="$packageMapName"
|
|
local existingVersion="${packageMapRef[$packageName]:-}"
|
|
|
|
if [[ -z "$existingVersion" ]] || (( $(vercmp "$packageVersion" "$existingVersion") > 0 )); then
|
|
packageMapRef["$packageName"]="$packageVersion"
|
|
fi
|
|
}
|
|
|
|
read_package_metadata() {
|
|
local packageFile="$1"
|
|
|
|
pacman -Qip "$packageFile" | parse_pacman_info
|
|
}
|
|
|
|
parse_pacman_info() {
|
|
awk '
|
|
/^Name[[:space:]]*:/ {
|
|
packageName=$0
|
|
sub(/^Name[[:space:]]*:[[:space:]]*/, "", packageName)
|
|
}
|
|
/^Version[[:space:]]*:/ {
|
|
packageVersion=$0
|
|
sub(/^Version[[:space:]]*:[[:space:]]*/, "", packageVersion)
|
|
}
|
|
END {
|
|
if (packageName == "" || packageVersion == "") {
|
|
exit 1
|
|
}
|
|
|
|
printf "%s\t%s\n", packageName, packageVersion
|
|
}
|
|
'
|
|
}
|
|
|
|
collect_local_packages() {
|
|
local packageMapName="$1"
|
|
# shellcheck disable=SC2178
|
|
local -n packageMapRef="$packageMapName"
|
|
local archDir packageFile metadata packageName packageVersion
|
|
local -a archDirs=("$repoDir/x86_64" "$repoDir/aarch64")
|
|
|
|
for archDir in "${archDirs[@]}"; do
|
|
if [[ ! -d "$archDir" ]]; then
|
|
continue
|
|
fi
|
|
|
|
for packageFile in "$archDir"/*.pkg.tar.zst "$archDir"/*.pkg.tar.xz; do
|
|
metadata="$(read_package_metadata "$packageFile")" || {
|
|
printf 'Unable to read package metadata: %s\n' "$packageFile" >&2
|
|
exit 1
|
|
}
|
|
|
|
packageName="${metadata%%$'\t'*}"
|
|
packageVersion="${metadata#*$'\t'}"
|
|
record_local_package_version "$packageName" "$packageVersion" "$packageMapName"
|
|
done
|
|
done
|
|
}
|
|
|
|
extract_aur_version_from_json() {
|
|
local packageName="$1"
|
|
|
|
jq -r --arg packageName "$packageName" '
|
|
.results[]
|
|
| select(.Name == $packageName)
|
|
| .Version
|
|
' | head -n1
|
|
}
|
|
|
|
fetch_aur_version() {
|
|
local packageName="$1"
|
|
|
|
curl -fsS --get \
|
|
--data-urlencode "arg[]=${packageName}" \
|
|
"$aurRpcUrl" |
|
|
extract_aur_version_from_json "$packageName"
|
|
}
|
|
|
|
print_outdated_packages() {
|
|
local packageMapName="$1"
|
|
# shellcheck disable=SC2178
|
|
local -n packageMapRef="$packageMapName"
|
|
local packageName localVersion aurVersion
|
|
local -a packageNames=()
|
|
|
|
mapfile -t packageNames < <(printf '%s\n' "${!packageMapRef[@]}" | sort)
|
|
|
|
for packageName in "${packageNames[@]}"; do
|
|
if is_excluded "$packageName"; then
|
|
continue
|
|
fi
|
|
|
|
localVersion="${packageMapRef[$packageName]}"
|
|
aurVersion="$(fetch_aur_version "$packageName" || true)"
|
|
|
|
if [[ -z "$aurVersion" ]]; then
|
|
continue
|
|
fi
|
|
|
|
if (( $(vercmp "$localVersion" "$aurVersion") < 0 )); then
|
|
printf '%s %s\n' "$packageName" "$aurVersion"
|
|
fi
|
|
done
|
|
}
|
|
|
|
assert_equals() {
|
|
local expected="$1"
|
|
local actual="$2"
|
|
local message="$3"
|
|
|
|
if [[ "$expected" != "$actual" ]]; then
|
|
printf 'FAIL: %s\nExpected: %s\nActual: %s\n' "$message" "$expected" "$actual" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
assert_success() {
|
|
local message="$1"
|
|
shift
|
|
|
|
if ! "$@"; then
|
|
printf 'FAIL: %s\n' "$message" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
assert_failure() {
|
|
local message="$1"
|
|
shift
|
|
|
|
if "$@"; then
|
|
printf 'FAIL: %s\n' "$message" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
if [[ "${1:-}" == "--self-test" ]]; then
|
|
run_self_tests
|
|
return
|
|
fi
|
|
|
|
require_cmd "curl"
|
|
require_cmd "jq"
|
|
require_cmd "pacman"
|
|
require_cmd "vercmp"
|
|
|
|
if [[ ! -d "$repoDir" ]]; then
|
|
printf 'Repo dir does not exist: %s\n' "$repoDir" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# shellcheck disable=SC2034
|
|
declare -A localPackages=()
|
|
collect_local_packages localPackages
|
|
print_outdated_packages localPackages
|
|
}
|
|
|
|
run_self_tests() {
|
|
declare -A packageMap=()
|
|
local exactMatchJson noMatchJson
|
|
local extractedVersion=""
|
|
local pacmanInfo=""
|
|
|
|
assert_success "excluded package should match" is_excluded "gzdoom"
|
|
assert_failure "non-excluded package should not match" is_excluded "fenrir"
|
|
|
|
record_local_package_version "fenrir" "1:2026.01.20-1" packageMap
|
|
record_local_package_version "fenrir" "1:2026.01.28-1" packageMap
|
|
record_local_package_version "fenrir" "1:2026.01.10-1" packageMap
|
|
assert_equals "1:2026.01.28-1" "${packageMap[fenrir]}" "newest local version should win"
|
|
|
|
exactMatchJson='{"results":[{"Name":"fenrir-git","Version":"1:r3322.4672592d-1"},{"Name":"fenrir","Version":"1:2026.01.28-1"}]}'
|
|
extractedVersion="$(printf '%s\n' "$exactMatchJson" | extract_aur_version_from_json "fenrir")"
|
|
assert_equals "1:2026.01.28-1" "$extractedVersion" "exact package name should be selected from AUR JSON"
|
|
|
|
noMatchJson='{"results":[{"Name":"fenrir-git","Version":"1:r3322.4672592d-1"}]}'
|
|
extractedVersion="$(printf '%s\n' "$noMatchJson" | extract_aur_version_from_json "fenrir")"
|
|
assert_equals "" "$extractedVersion" "missing exact AUR match should stay empty"
|
|
|
|
pacmanInfo='Name : fenrir
|
|
Version : 1:2026.01.28-1
|
|
Description : A user space console screen reader written in python3'
|
|
extractedVersion="$(printf '%s\n' "$pacmanInfo" | parse_pacman_info)"
|
|
assert_equals $'fenrir\t1:2026.01.28-1' "$extractedVersion" "pacman metadata parsing should return name and version"
|
|
|
|
if (( $(vercmp "1:2026.01.20-1" "1:2026.01.28-1") >= 0 )); then
|
|
printf 'FAIL: older local version should compare lower than AUR version\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
printf 'Self-test passed\n'
|
|
}
|
|
|
|
main "$@"
|