From f854e042a6dd8421f39d8fee1befc111bee52018 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 11 Oct 2026 14:46:10 +0900 Subject: [PATCH] Move exact trigger pins when sync-rebuilds bumps a package A package built against a trigger's ABI pins it exactly, such as a Hyprland plugin on hyprland=0.56.2. Rebuilding it against a newer trigger left the pin behind, so the rebuilt package demanded the version it no longer loads into. sync-rebuilds now moves pins in the per-architecture depends arrays to the version each architecture rebuilds against. Co-Authored-By: Claude Opus 5.5 --- bin/sync-rebuilds | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/bin/sync-rebuilds b/bin/sync-rebuilds index 32fc7751..010ff6f8 100755 --- a/bin/sync-rebuilds +++ b/bin/sync-rebuilds @@ -329,6 +329,43 @@ write_pkgrel() { mv "$scratch" "$pkgbuild" } +# A package built against a trigger's ABI pins it exactly, once per architecture +# because Arch Linux ARM can trail x86_64: depends_x86_64=('hyprland=0.56.2'). +# Rebuilding against a newer trigger has to move that pin with it, or the +# rebuilt package demands the version it no longer loads into. Unpinned +# dependencies, and pins outside the per-architecture arrays, are left alone. +write_pins() { + local package_dir="$1" + local current="$2" + local pkgbuild="$package_dir/PKGBUILD" + local scratch="$pkgbuild.sync-rebuilds" + local arch trigger version pattern + + cp "$pkgbuild" "$scratch" || return 1 + while IFS=$'\t' read -r arch trigger version; do + pattern=$(printf '%s' "$trigger" | sed 's/[][\.*^$+?(){}|/]/\\&/g') + if ! awk -v array="depends_$arch" -v pattern="$pattern" -v pin="$trigger=$(version_pkgver "$version")" ' + $0 ~ "^" array "=\\(" { inside = 1 } + inside { gsub("[\"\047]" pattern "=[^\"\047]*[\"\047]", "\047" pin "\047") } + inside && /\)/ { inside = 0 } + { print } + ' "$scratch" > "$scratch.new"; then + rm -f "$scratch" "$scratch.new" + return 1 + fi + mv "$scratch.new" "$scratch" + done < <(jq -r 'to_entries[] | .key as $arch | .value | to_entries[] | [$arch, .key, .value] | @tsv' <<<"$current") + + if ! bash -n "$scratch" 2>/dev/null; then + print_error "PKGBUILD with moved pins is not valid shell" + rm -f "$scratch" + return 1 + fi + + chmod --reference="$pkgbuild" "$scratch" + mv "$scratch" "$pkgbuild" +} + write_metadata() { local package_dir="$1" local filter="$2" @@ -551,6 +588,12 @@ sync_package() { return 0 fi + if ! write_pins "$package_dir" "$current"; then + print_error " Failed to move pinned trigger versions for $package" + ((++FAILED)) + return 0 + fi + if [[ -n "$suffix" ]] && ! write_metadata "$package_dir" '.pkgrel.suffix = ($suffix | tonumber)' --arg suffix "$suffix"; then print_error " Failed to record pkgrel suffix for $package" ((++FAILED)) @@ -792,6 +835,17 @@ cmd_self_test() { check "run succeeds" 0 "$(run_case "$root")" check "pkgrel untouched" 1 "$(pkgrel_of "$root/pkgbuilds/t-current")" + echo "A rebuild moves the trigger's exact pin for the architecture it ran for:" + root=$(selftest_root pins) + selftest_package "$root" t-pins 1 '{"source":"local","rebuild_on":["dep-a"],"rebuilt_against":{"dep-a":"1.0-1"}}' + printf "depends_x86_64=('dep-a=1.0' 'dep-b')\ndepends_aarch64=('dep-a=1.0')\n" >> "$root/pkgbuilds/t-pins/PKGBUILD" + selftest_pacman "$root" dep-a=2.0-1 + selftest_published "$root" + check "run succeeds" 0 "$(run_case "$root")" + check "pkgrel bumped" 2 "$(pkgrel_of "$root/pkgbuilds/t-pins")" + check "x86_64 pin moved" "depends_x86_64=('dep-a=2.0' 'dep-b')" "$(grep '^depends_x86_64=' "$root/pkgbuilds/t-pins/PKGBUILD")" + check "aarch64 pin left for its own run" "depends_aarch64=('dep-a=1.0')" "$(grep '^depends_aarch64=' "$root/pkgbuilds/t-pins/PKGBUILD")" + echo "Metadata that does not parse fails the run instead of vanishing from it:" root=$(selftest_root unreadable) selftest_package "$root" t-broken 1 '{"source":"local","rebuild_on":["dep-a"'