You've already forked .autoupdate
Some checks failed
Build Autoupdater Image / build (push) Failing after 6s
All six nightly jobs ran the same pacman transaction before doing any work. The toolchain now lives in an image published to the Gitea registry as git.bjphoster.com/aur/autoupdate:latest, rebuilt monthly since Arch is rolling and a month-old image is a month behind. The build job itself runs on plain ubuntu-latest -- it only drives docker. Only the image is Arch. Based on archlinux:base rather than base-devel: the autoupdater parses PKGBUILDs and never compiles, so the toolchain is dead weight, 822 MB against 1.48 GB. nodejs is included because actions/checkout is a JavaScript action the runner executes with node from inside the image. Slimming to base initially dropped diffutils, and apply_pkgrel_rule used diff -q. A missing binary makes the `if` fail, which reads as "changed", so pkgrel ratcheted upward on every run -- the same silent-wrong-default class as the empty .SRCINFO this rewrite was about, and invisible to the host test run. The comparison is now a shell string equality with no external dependency, and is verified against an image with no diff at all. require_tools() additionally fails on startup for any missing binary rather than midway through. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
275 lines
10 KiB
Bash
Executable File
275 lines
10 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Nightly AUR package reconciler.
|
|
#
|
|
# usage: autoupdate.sh <package> [...]
|
|
#
|
|
# env:
|
|
# GIT_HOST git host holding the package repos (default git.bjphoster.com)
|
|
# WORKDIR where to clone; a temp dir by default
|
|
# NO_GIT_PUSH=1 do everything except push (dry run)
|
|
# FORCE_REBUILD=1 bump pkgrel even with no detected change
|
|
# FORCE_REFRESH=1 re-download remote sources even without a version change
|
|
# AUR_FORCE=1 force-push over diverged AUR history
|
|
#
|
|
# This is a RECONCILER, not a version-bump script. A new upstream release is
|
|
# only one of the things that can make a package diverge from the AUR; a manual
|
|
# push to origin is another, and a failed push last night is a third. All three
|
|
# funnel through the same path, and the AUR reconcile at the end runs
|
|
# unconditionally.
|
|
|
|
set -euo pipefail
|
|
|
|
_here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck source=common.sh
|
|
source "${_here}/common.sh"
|
|
|
|
require_noninteractive
|
|
require_tools
|
|
|
|
GIT_HOST="${GIT_HOST:-git.bjphoster.com}"
|
|
# Where the package repos live, and how the upstream version is resolved.
|
|
# Both are overridable so the test suite can run hermetically against local
|
|
# bare repos; the defaults are the production values.
|
|
PKG_REPO_BASE="${PKG_REPO_BASE:-https://${GIT_HOST}/aur}"
|
|
LATEST_VERSION_CMD="${LATEST_VERSION_CMD:-${_here}/latest-version.sh}"
|
|
|
|
[ "$#" -ge 1 ] || die "usage: autoupdate.sh <package> [...]"
|
|
|
|
WORKDIR="${WORKDIR:-$(mktemp -d)}"
|
|
mkdir -p "$WORKDIR"
|
|
|
|
update_one() {
|
|
local pkg="$1"
|
|
local dir="${WORKDIR}/${pkg}"
|
|
|
|
log "=== ${pkg} ==="
|
|
|
|
if [ -d "$dir/.git" ]; then
|
|
git -C "$dir" fetch --quiet origin
|
|
git -C "$dir" reset --quiet --hard origin/HEAD
|
|
else
|
|
rm -rf "$dir"
|
|
git clone --quiet "${PKG_REPO_BASE}/${pkg}" "$dir" \
|
|
|| die "could not clone ${pkg} from ${PKG_REPO_BASE}"
|
|
fi
|
|
|
|
cd "$dir"
|
|
[ -f PKGBUILD ] || die "${pkg} has no PKGBUILD"
|
|
[ -f .autoupdate ] || die "${pkg} has no .autoupdate manifest"
|
|
|
|
# shellcheck disable=SC1091
|
|
source ./.autoupdate
|
|
|
|
# Per-run download cache, shared between the AppImage extraction below and
|
|
# update-checksums.sh so a 100 MB AppImage is fetched at most once.
|
|
export DL_CACHE="${WORKDIR}/.cache"
|
|
mkdir -p "$DL_CACHE"
|
|
|
|
# --- AUR reference state -------------------------------------------------
|
|
# Fetched up front: it is the reference for the pkgrel decision, not just a
|
|
# push target. Comparing against the AUR rather than against local history is
|
|
# what makes the pkgrel bump idempotent -- once pushed, the AUR matches and
|
|
# the next run finds nothing to do.
|
|
# Read-only HTTPS: establishing the reference state needs no SSH key, so
|
|
# dry runs work anywhere. Only sync-aur.sh's push is authenticated.
|
|
local aur_pkgbuild="" aur_pkgrel="" aur_pkgver="" aur_name
|
|
aur_name="$(pkgbuild_expand PKGBUILD 'printf %s "$pkgname"')"
|
|
git remote remove aur-ro >/dev/null 2>&1 || true
|
|
git remote add aur-ro "${AUR_HTTPS_BASE}/${aur_name}.git"
|
|
if git fetch --quiet aur-ro master 2>/dev/null; then
|
|
aur_pkgbuild="${WORKDIR}/${pkg}.aur.PKGBUILD"
|
|
if git show FETCH_HEAD:PKGBUILD > "$aur_pkgbuild" 2>/dev/null; then
|
|
aur_pkgrel="$(pkgbuild_get pkgrel "$aur_pkgbuild")"
|
|
aur_pkgver="$(pkgbuild_get pkgver "$aur_pkgbuild")"
|
|
info "AUR has ${aur_pkgver}-${aur_pkgrel}"
|
|
else
|
|
aur_pkgbuild=""
|
|
fi
|
|
else
|
|
info "not on the AUR yet"
|
|
fi
|
|
|
|
# --- upstream version ----------------------------------------------------
|
|
# A failure in here exits non-zero and is NOT mistaken for "no new version".
|
|
local oldver newver
|
|
oldver="$(pkgbuild_get pkgver)"
|
|
newver="$("$LATEST_VERSION_CMD" .)"
|
|
info "upstream ${newver}, local ${oldver:-<empty>}"
|
|
|
|
local version_changed=0
|
|
if [ "$newver" != "$oldver" ]; then
|
|
version_changed=1
|
|
log "new upstream version ${oldver:-<empty>} -> ${newver}"
|
|
pkgbuild_set pkgver "$newver"
|
|
# pkgrel is not touched here; apply_pkgrel_rule derives it from the AUR.
|
|
fi
|
|
|
|
# --- AppImage assets -----------------------------------------------------
|
|
if [ "${gui:-false}" = "true" ] && [ "$version_changed" -eq 1 ]; then
|
|
extract_appimage_assets
|
|
fi
|
|
|
|
# --- checksums + .SRCINFO ------------------------------------------------
|
|
# Local sources are always re-hashed (free, no network) so a hand-edited
|
|
# .desktop or icon self-corrects. Remote sources are only re-fetched when the
|
|
# version moved, or on demand.
|
|
local cs_args=()
|
|
if [ "$version_changed" -eq 1 ] || [ -n "${FORCE_REFRESH:-}" ]; then
|
|
cs_args+=(--remote)
|
|
fi
|
|
"${_here}/update-checksums.sh" "${cs_args[@]}" .
|
|
|
|
regen_srcinfo .
|
|
|
|
# --- pkgrel ---------------------------------------------------------------
|
|
apply_pkgrel_rule
|
|
|
|
# --- commit ---------------------------------------------------------------
|
|
git add -A
|
|
if git diff --cached --quiet; then
|
|
# Nothing changed. Deliberately do NOT exit here: the AUR may still be out
|
|
# of sync from a previous failed push, and an unguarded `git commit` with
|
|
# nothing staged would exit non-zero and, under `set -e`, abort before the
|
|
# reconcile.
|
|
log "no local changes for ${pkg}"
|
|
else
|
|
local ver rel
|
|
ver="$(pkgbuild_get pkgver)"; rel="$(pkgbuild_get pkgrel)"
|
|
git commit --quiet -m "Updated ${pkg} to ${ver}-${rel}"
|
|
log "committed ${pkg} ${ver}-${rel}"
|
|
fi
|
|
|
|
# --- publish --------------------------------------------------------------
|
|
# AUR first: if it rejects the push, origin is left unpoisoned and the whole
|
|
# thing is recomputed cleanly on the next run.
|
|
"${_here}/sync-aur.sh" .
|
|
|
|
if [ -n "${NO_GIT_PUSH:-}" ]; then
|
|
log "NO_GIT_PUSH set: not pushing ${pkg} to origin"
|
|
else
|
|
git push --quiet origin HEAD
|
|
fi
|
|
}
|
|
|
|
# Derive pkgrel so that fixing a package() bug and pushing needs no manual bump.
|
|
apply_pkgrel_rule() {
|
|
local cur_ver cur_rel
|
|
cur_ver="$(pkgbuild_get pkgver)"
|
|
cur_rel="$(pkgbuild_get pkgrel)"
|
|
|
|
# Nothing on the AUR to compare against (new package): push as-is.
|
|
if [ -z "${aur_pkgbuild:-}" ]; then
|
|
return 0
|
|
fi
|
|
|
|
# pkgrel is derived purely from the AUR reference, because the AUR is what
|
|
# `-N` is numbered against. If the version we are about to publish differs
|
|
# from the published one, this is a new upstream version as far as the AUR is
|
|
# concerned and the packaging revision restarts at 1 -- whether the bump
|
|
# happened in this run or in an earlier one that failed to push. That second
|
|
# case is real: reflex-appimage sat at 1.0.11-4 locally against 1.0.10-4 on
|
|
# the AUR, because the old script only reset pkgrel under FORCE_REBUILD.
|
|
if [ "$cur_ver" != "${aur_pkgver:-}" ]; then
|
|
if [ "$cur_rel" != "1" ]; then
|
|
pkgbuild_set pkgrel "1"
|
|
log "new version relative to the AUR (${aur_pkgver} -> ${cur_ver}); pkgrel ${cur_rel} -> 1"
|
|
regen_srcinfo .
|
|
fi
|
|
return 0
|
|
fi
|
|
|
|
local base_rel="${aur_pkgrel:-0}"
|
|
[[ "$base_rel" =~ ^[0-9]+$ ]] || base_rel=0
|
|
|
|
if [ -n "${FORCE_REBUILD:-}" ]; then
|
|
pkgbuild_set pkgrel "$(( base_rel + 1 ))"
|
|
log "FORCE_REBUILD: pkgrel -> $(( base_rel + 1 ))"
|
|
regen_srcinfo .
|
|
return 0
|
|
fi
|
|
|
|
# Compare with the pkgrel line stripped, so the decision is about substantive
|
|
# packaging content. An edit to a local source lands here too, because its
|
|
# sha256sums entry was just regenerated. A README-only edit does not touch
|
|
# the PKGBUILD and so correctly does not bump.
|
|
#
|
|
# Deliberately a shell string comparison rather than diff(1): diff lives in
|
|
# diffutils, which archlinux:base does not ship, and a missing binary makes
|
|
# the `if` fail, which reads as "changed" and ratchets pkgrel upward on every
|
|
# single run. Same silent-wrong-default class of bug as the empty .SRCINFO.
|
|
local local_body aur_body
|
|
local_body="$(grep -v '^pkgrel=' PKGBUILD)"
|
|
aur_body="$(grep -v '^pkgrel=' "$aur_pkgbuild")"
|
|
if [ "$local_body" = "$aur_body" ]; then
|
|
return 0 # identical packaging; nothing to do
|
|
fi
|
|
|
|
if [ "$cur_rel" = "$aur_pkgrel" ]; then
|
|
pkgbuild_set pkgrel "$(( base_rel + 1 ))"
|
|
log "packaging changed without a pkgrel bump; pkgrel -> $(( base_rel + 1 ))"
|
|
regen_srcinfo .
|
|
else
|
|
info "pkgrel already moved by hand (${aur_pkgrel} -> ${cur_rel}); leaving it"
|
|
fi
|
|
}
|
|
|
|
# Pull the .desktop and icon out of the AppImage so they can be shipped as
|
|
# local sources. Unchanged in substance from the old push.sh.
|
|
extract_appimage_assets() {
|
|
local host_arr="source_$(uname -m)" entry loc img newdesktop newicon
|
|
local candidates=()
|
|
|
|
# A multi-arch AppImage is extracted once, against the host arch's image.
|
|
if pkgbuild_has_array PKGBUILD "$host_arr"; then
|
|
mapfile -t candidates < <(pkgbuild_array PKGBUILD "$host_arr")
|
|
fi
|
|
if [ "${#candidates[@]}" -eq 0 ]; then
|
|
mapfile -t candidates < <(pkgbuild_array PKGBUILD source)
|
|
fi
|
|
|
|
img=""
|
|
for entry in "${candidates[@]}"; do
|
|
loc="$(source_loc "$entry")"
|
|
if printf '%s' "${entry,,}" | grep -q "appimage" && is_remote "$entry"; then
|
|
img="$(download_cached "$loc")"
|
|
break
|
|
fi
|
|
done
|
|
[ -n "$img" ] || { warn "gui=true but no AppImage source found; skipping asset extraction"; return 0; }
|
|
|
|
local pkgbase
|
|
pkgbase="$(pkgbuild_expand PKGBUILD 'printf %s "${_pkgname:-$pkgname}"')"
|
|
newdesktop="${pkgbase}.desktop"
|
|
newicon="${pkgbase}.png"
|
|
|
|
local tmp; tmp="$(mktemp -d)"
|
|
cp "$img" "${tmp}/app.AppImage"
|
|
chmod +x "${tmp}/app.AppImage"
|
|
( cd "$tmp" && ./app.AppImage --appimage-extract >/dev/null 2>&1 ) \
|
|
|| { warn "could not extract ${pkgbase} AppImage; keeping existing assets"; rm -rf "$tmp"; return 0; }
|
|
|
|
if [ -f "${tmp}/squashfs-root/${desktop}" ]; then
|
|
cp "${tmp}/squashfs-root/${desktop}" "$newdesktop"
|
|
sed -i "/^X-AppImage-/d;/^$/d" "$newdesktop"
|
|
sed -i "s|^Exec=.*|Exec=${pkgbase} %U|" "$newdesktop"
|
|
info "extracted ${newdesktop}"
|
|
else
|
|
warn "${desktop} not found inside the AppImage"
|
|
fi
|
|
|
|
if [ -f "${tmp}/squashfs-root/${icon}" ]; then
|
|
cp "${tmp}/squashfs-root/${icon}" "$newicon"
|
|
info "extracted ${newicon}"
|
|
else
|
|
warn "${icon} not found inside the AppImage"
|
|
fi
|
|
|
|
rm -rf "$(readlink -f "${tmp}/squashfs-root" 2>/dev/null || true)" "$tmp"
|
|
}
|
|
|
|
rc=0
|
|
for pkg in "$@"; do
|
|
( update_one "$pkg" ) || { rc=1; warn "${pkg} FAILED"; }
|
|
done
|
|
exit "$rc"
|