You've already forked .autoupdate
The six nightly workflows each cloned a package repo and ran that repo's own
push.sh. Two problems compounded into months of silent breakage.
push.sh pushed to origin before the AUR. When the AUR push failed, origin
already carried the new version, so every later run hit "same (old) version
specified", bare-exit 0'd, and the workflow went green while the AUR rotted.
reflex-appimage sat at 1.0.11 locally against 1.0.10 published; nethlink-appimage
was never submitted at all.
The AUR pushes were failing because .SRCINFO was empty -- 0 bytes in
reflex-appimage, 1 byte in nethlink-appimage and pman-helper, against 500-2000
in the healthy ones. The AUR rejects an invalid .SRCINFO. Both PKGBUILDs verify
clean under a real Arch makepkg, so this was the CI environment, and with no
set -e the script committed and pushed the truncated file regardless.
pman-helper failed independently: the workflows queried the Gitea endpoint
/repos/{owner}/{repo}/repo/tags, which 404s -- the correct path is /tags. The
empty result fell through to an interactive read that got EOF, and it committed
pkgver="" plus the sha256 of a zero-byte download.
The replacement is a reconciler rather than a version-bump script. A new
upstream release, a manual push to origin, and a push that failed last night
all take the same path, and the AUR reconcile runs on every pass -- so a failed
push simply retries. Sync is decided by comparing tree content against the AUR,
not by whether this run happened to find something new.
pkgrel is now derived from the AUR rather than from local history, which is
what makes it idempotent: a version differing from the published one resets it
to 1, a packaging change increments it, and once published the next run sees no
difference and does nothing. Editing a PKGBUILD no longer needs a manual bump.
Checksums are computed directly instead of via makepkg -g / updpkgsums. Those
generate every arch array in one pass into a single directory, so when two
arches rename to the same target the second finds the first one's file and
emits an identical, silently wrong sum -- verified against freetube-appimage.
Downloads go to content-addressed cache paths instead. Multi-arch works for
when it is needed; single-arch is the degenerate case of the same loop.
Guards for each observed failure: a real Arch makepkg in an archlinux container
as non-root, set -euo pipefail throughout, .SRCINFO validated against the
PKGBUILD before any commit, and a failed version lookup exiting 1 so it can
never again be mistaken for a quiet "no new version" night.
Package-specific detail moves out of this repo entirely, into an .autoupdate
manifest committed to each package repo. sha256 only; sha1sums and md5sums are
removed. deskflow-bin and freetube-appimage are dropped from automation, having
been deleted from the AUR.
Covered by test.sh: 37 assertions against local bare repos and a local HTTP
server, no network and never touching the real AUR. The retry, pkgrel
idempotence and multi-arch collision tests were each verified by mutation.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
106 lines
3.5 KiB
Bash
Executable File
106 lines
3.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Recompute sha256sums for a PKGBUILD, single- or multi-arch.
|
|
#
|
|
# usage: update-checksums.sh [--remote] [package-dir]
|
|
#
|
|
# --remote also re-download and re-hash remote sources.
|
|
# Without it only local repo files (.desktop, icons) are
|
|
# re-hashed, which needs no network and is what lets a
|
|
# hand-edited repo self-correct on every nightly run.
|
|
#
|
|
# sha256 only: sha1sums/md5sums are retired and actively removed.
|
|
#
|
|
# Why this does not shell out to `makepkg -g` / `updpkgsums`: those generate
|
|
# every arch array in one pass and download all arches into a single directory.
|
|
# When two arches rename to the same target (`foo.AppImage::url-amd64`,
|
|
# `foo.AppImage::url-arm64`) the second arch finds the first arch's file
|
|
# already present, skips the download, and emits an sha256sums_aarch64
|
|
# identical to sha256sums_x86_64 -- silently wrong. Overriding CARCH does not
|
|
# help; makepkg.conf resets it, and --config still downloads every arch.
|
|
|
|
set -euo pipefail
|
|
|
|
_here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck source=common.sh
|
|
source "${_here}/common.sh"
|
|
|
|
refresh_remote=0
|
|
if [ "${1-}" = "--remote" ]; then refresh_remote=1; shift; fi
|
|
|
|
pkgdir="${1:-.}"
|
|
pkgbuild="${pkgdir}/PKGBUILD"
|
|
[ -f "$pkgbuild" ] || die "no PKGBUILD in ${pkgdir}"
|
|
|
|
export DL_CACHE="${DL_CACHE:-$(mktemp -d)}"
|
|
|
|
mapfile -t arches < <(pkgbuild_array "$pkgbuild" arch)
|
|
|
|
# Build the work list: every source array present, paired with its sums array.
|
|
# Single-arch is just the degenerate case of the same loop -- no special path.
|
|
pairs=()
|
|
if pkgbuild_has_array "$pkgbuild" source; then
|
|
pairs+=("source:sha256sums")
|
|
fi
|
|
for a in "${arches[@]}"; do
|
|
[ "$a" = "any" ] && continue
|
|
if pkgbuild_has_array "$pkgbuild" "source_${a}"; then
|
|
pairs+=("source_${a}:sha256sums_${a}")
|
|
fi
|
|
done
|
|
|
|
[ "${#pairs[@]}" -gt 0 ] || die "PKGBUILD declares no source arrays"
|
|
|
|
for pair in "${pairs[@]}"; do
|
|
src_arr="${pair%%:*}"
|
|
sum_arr="${pair##*:}"
|
|
|
|
mapfile -t entries < <(pkgbuild_array "$pkgbuild" "$src_arr")
|
|
[ "${#entries[@]}" -gt 0 ] || continue
|
|
|
|
mapfile -t existing < <(pkgbuild_array "$pkgbuild" "$sum_arr")
|
|
|
|
# If the existing sums don't line up with the sources we cannot reuse any of
|
|
# them by index, so everything must be recomputed.
|
|
reuse=1
|
|
if [ "${#existing[@]}" -ne "${#entries[@]}" ]; then reuse=0; fi
|
|
|
|
log "${src_arr} -> ${sum_arr} (${#entries[@]} source(s))"
|
|
|
|
sums=()
|
|
for i in "${!entries[@]}"; do
|
|
entry="${entries[$i]}"
|
|
loc="$(source_loc "$entry")"
|
|
|
|
if is_vcs "$entry"; then
|
|
sum="SKIP"
|
|
info "$(source_name "$entry") SKIP (vcs)"
|
|
elif is_remote "$entry"; then
|
|
if [ "$refresh_remote" -eq 1 ] || [ "$reuse" -eq 0 ] \
|
|
|| [ -z "${existing[$i]:-}" ] || [ "${existing[$i]}" = "SKIP" ]; then
|
|
path="$(download_cached "$loc")"
|
|
sum="$(sha256sum "$path" | awk '{print $1}')"
|
|
info "$(source_name "$entry") ${sum}"
|
|
else
|
|
sum="${existing[$i]}"
|
|
info "$(source_name "$entry") ${sum} (kept)"
|
|
fi
|
|
else
|
|
# A file that lives in the package repo itself.
|
|
[ -f "${pkgdir}/${loc}" ] || die "local source '${loc}' not found in ${pkgdir}"
|
|
sum="$(sha256sum "${pkgdir}/${loc}" | awk '{print $1}')"
|
|
info "${loc} ${sum} (local)"
|
|
fi
|
|
sums+=("$sum")
|
|
done
|
|
|
|
replace_array "$pkgbuild" "$sum_arr" "${sums[@]}"
|
|
done
|
|
|
|
# Retire the legacy digests wherever they still appear.
|
|
for a in "" "${arches[@]}"; do
|
|
suffix="${a:+_$a}"
|
|
for algo in sha1sums md5sums sha224sums sha384sums sha512sums b2sums; do
|
|
remove_array "$pkgbuild" "${algo}${suffix}"
|
|
done
|
|
done
|