common: allow automatic branch following for extensions

When an application or runtime is updated and its metadata requests a
new branch of an extension, Flatpak should automatically pull the new
branch if the user already has at least one branch of that extension
installed.

This ensures that "no-autodownload" extensions (like GIMP plugins)
stay functional after an update that requires a new branch, while still
respecting the user's explicit opt-in (the existing installation of
a previous branch).

Fixes: https://github.com/flatpak/flatpak/issues/4208
This commit is contained in:
Kolja Lampe
2026-03-26 17:31:45 +00:00
committed by Sebastian Wick
parent f2ff272157
commit 3178d97321
4 changed files with 129 additions and 0 deletions

View File

@@ -16573,6 +16573,26 @@ add_related (FlatpakDir *self,
flatpak_extension_matches_reason (id, download_if, !no_autodownload) ||
deploy_data != NULL;
/* Automatic branch following: if this extension wouldn't normally be
* auto-downloaded, still download it if there's already an installed branch
* of the same extension for this arch. This handles the case where an app
* updates its extension version requirement. */
if (!download)
{
g_autoptr(GPtrArray) installed_branches =
flatpak_dir_list_refs_for_name (self, FLATPAK_KINDS_RUNTIME, id, NULL, NULL);
for (size_t i = 0; installed_branches && i < installed_branches->len; i++)
{
FlatpakDecomposed *installed_ref = g_ptr_array_index (installed_branches, i);
if (flatpak_decomposed_is_arch (installed_ref, arch))
{
download = TRUE;
break;
}
}
}
if (!flatpak_extension_matches_reason (id, autoprune_unless, TRUE))
auto_prune = TRUE;

View File

@@ -0,0 +1,107 @@
#!/bin/bash
set -euo pipefail
# shellcheck source=tests/libtest.sh
# shellcheck disable=SC1091
. "$(dirname "$0")/libtest.sh"
skip_without_bwrap
echo "1..1"
setup_repo () {
mkdir -p repos
ostree init --repo=repos/test --mode=archive-z2
}
make_extension () {
local ID=$1
local VERSION=$2
local DIR
DIR=$(mktemp -d)
cat > "${DIR}/metadata" <<EOF
[Runtime]
name=${ID}
EOF
mkdir -p "${DIR}/usr"
mkdir -p "${DIR}/files"
touch "${DIR}/usr/extension-$ID:$VERSION"
# shellcheck disable=SC2086
${FLATPAK} build-export --no-update-summary --runtime ${GPGARGS-} repos/test "${DIR}" "${VERSION}" >&2
update_repo
rm -rf "${DIR}"
}
setup_repo
"$(dirname "$0")/make-test-runtime.sh" repos/test org.test.Platform master "" "" bash ls cat echo readlink > /dev/null
# Create app version 1 with extension version 1.0
mkdir -p hello
cat > hello/metadata <<EOF
[Application]
name=org.test.Hello
runtime=org.test.Platform/$(uname -m)/master
sdk=org.test.Platform/$(uname -m)/master
[Extension org.test.Extension]
directory=files/ext
version=1.0
no-autodownload=true
EOF
mkdir -p hello/files/ext
${FLATPAK} build-finish --no-inherit-permissions hello >&2
${FLATPAK} build-export --no-update-summary --disable-sandbox repos/test hello master >&2
update_repo
# Create extension branch 1.0
make_extension org.test.Extension 1.0
# Install app and extension 1.0
${FLATPAK} remote-add --user --no-gpg-verify test-repo repos/test >&2
${FLATPAK} --user install -y test-repo org.test.Hello master >&2
${FLATPAK} --user install -y test-repo org.test.Extension 1.0 >&2
if ! ${FLATPAK} --user list --runtime | grep -q "org.test.Extension.*1.0"; then
${FLATPAK} --user list --runtime >&2
assert_not_reached "Extension 1.0 not installed"
fi
echo "# Extension 1.0 installed successfully" >&2
# Create app version 2 with extension version 2.0
mkdir -p hello2
cat > hello2/metadata <<EOF
[Application]
name=org.test.Hello
runtime=org.test.Platform/$(uname -m)/master
sdk=org.test.Platform/$(uname -m)/master
[Extension org.test.Extension]
directory=files/ext
version=2.0
no-autodownload=true
EOF
mkdir -p hello2/files/ext
${FLATPAK} build-finish --no-inherit-permissions hello2 >&2
${FLATPAK} build-export --no-update-summary --disable-sandbox repos/test hello2 master >&2
update_repo
# Create extension branch 2.0
make_extension org.test.Extension 2.0
# Update the app
${FLATPAK} --user update -y --verbose org.test.Hello master >&2
# Check if extension 2.0 was installed
${FLATPAK} --user list --runtime >&2
if ${FLATPAK} --user list --runtime | grep -q "org.test.Extension.*2.0"; then
echo "# Success: Extension 2.0 installed automatically after branch follow"
else
assert_not_reached "Extension 2.0 NOT installed automatically"
fi
ok "extension branch follow"

View File

@@ -40,6 +40,7 @@ wrapped_tests += {'name' : 'test-history.sh', 'script' : 'test-history.sh'}
wrapped_tests += {'name' : 'test-default-remotes.sh', 'script' : 'test-default-remotes.sh'}
wrapped_tests += {'name' : 'test-metadata-validation.sh', 'script' : 'test-metadata-validation.sh'}
wrapped_tests += {'name' : 'test-extensions.sh', 'script' : 'test-extensions.sh'}
wrapped_tests += {'name' : 'test-extension-branch-follow.sh', 'script' : 'test-extension-branch-follow.sh'}
wrapped_tests += {'name' : 'test-oci.sh', 'script' : 'test-oci.sh'}
wrapped_tests += {'name' : 'test-override.sh', 'script' : 'test-override.sh'}
wrapped_tests += {'name' : 'test-auth.sh', 'script' : 'test-auth.sh'}

View File

@@ -21,6 +21,7 @@ TEST_MATRIX_SOURCE=(
'tests/test-default-remotes.sh' \
'tests/test-metadata-validation.sh' \
'tests/test-extensions.sh' \
'tests/test-extension-branch-follow.sh' \
'tests/test-bundle.sh{user+system+system-norevokefs}' \
'tests/test-oci.sh' \
'tests/test-oci-registry.sh{{user+system},{http+https}}' \