#!/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