From d9bcad79b7de8f1491e6309cd06f7d966245603d Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 Jun 2026 16:37:01 +0200 Subject: [PATCH] pacman-helper: tolerate removing a package absent from a database The `quick_remove` action recently started to fail when asked to drop a package that no longer lives in every database it touches. Removing the obsolete `git-credential-manager-core` packages aborted as soon as the removal reached the freshly-initialized, still-empty `ucrt64` database, with `repo-remove` reporting `Package matching '...' not found.` and `quick_remove` turning that into a fatal error. This only surfaced once we taught `quick_action` to run the removal across the second and third databases (`mingw64` and `ucrt64`) in addition to the architecture database. `repo-remove` is all-or-nothing: if any requested name is absent it leaves the database untouched and exits non-zero (in /usr/bin/repo-remove a failed `remove` sets `fail=1`, after which it prints `Package database was not modified due to errors.` and exits 1). A package that only ever lived in one of the databases therefore makes the whole operation fail, even though removing a package that is not there ought to be a harmless no-op. Simply ignoring `repo-remove`'s exit code is not an option: besides papering over genuine errors, the all-or-nothing behavior would also silently skip packages that *are* present alongside an absent one. Instead, I now filter the requested names against the database's actual contents (via the existing `package_list` helper, stripping the `--` suffix and matching whole names so that `git-credential-manager` is not mistaken for `git-credential-manager-core`) and only hand the survivors to `repo-remove`, skipping the call entirely when nothing is left to remove. This is the run that motivated the change: https://github.com/git-for-windows/git-for-windows-automation/actions/runs/27195671090 Assisted-by: Opus 4.8 Signed-off-by: Johannes Schindelin --- pacman-helper.sh | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/pacman-helper.sh b/pacman-helper.sh index 66829adf03..3905d7730a 100755 --- a/pacman-helper.sh +++ b/pacman-helper.sh @@ -131,15 +131,30 @@ repo_remove () { # Make sure that GPGKEY is used unquoted sed 's/"\(\${\?GPGKEY}\?\)"/\1/g' "$this_script_dir/repo-remove" fi && - "$this_script_dir/repo-remove" $(for arg - do - # repo-remove only accepts package _names_, but we are potentially given _files_. - # Handle this by distilling the package names from filenames. - case "$arg" in - *.pkg.tar.xz|*.pkg.tar.zst) echo "${arg%-*-*-*}";; - *) echo "$arg";; - esac - done) + # repo-remove only accepts package _names_, but we are potentially given + # _files_; distill the names from the file names. Furthermore, repo-remove + # fails the _entire_ operation (modifying nothing) when _any_ of the given + # names is absent from the database, e.g. a freshly-initialized, still-empty + # ucrt64 database. To allow removing a package even when it is not present in + # a given database, drop the names the database does not contain and skip the + # call altogether when nothing is left to remove. + dbfile="$1" && + shift && + present=" $(package_list "$dbfile" | sed 's/-[^-]*-[^-]*$//' | tr '\n' ' ')" && + names= && + { + for arg + do + case "$arg" in + *.pkg.tar.xz|*.pkg.tar.zst) name="${arg%-*-*-*}";; + *) name="$arg";; + esac + case "$present" in + *" $name "*) names="$names $name";; + esac + done + } && + { test -z "$names" || "$this_script_dir/repo-remove" "$dbfile" $names; } } update_versions_json () { #