Remove EOL runtimes upon app uninstall/upgrade

We normally don't remove a runtime when the last app using it is
uninstalled, since runtimes are large and re-downloading it in the
future may be difficult. But if the runtime is end-of-life, there's a
reasonable chance it won't be used again, so uninstall it in that case.

Similarly, if the last app using a runtime is upgraded to a different
runtime, and the runtime is EOL, uninstall it.

A unit test is included, and the subsequent unit test also had to be
modified. Otherwise we get a "Update is older than current version"
error, since the runtime is installed from test-repo but
setup_repo_no_add() calls make_runtime() which uses the one in
runtime-repo instead, which is older than the one in test-repo.
This commit is contained in:
Phaedrus Leeds
2020-08-07 18:49:32 -07:00
committed by Alexander Larsson
parent aa55d0088f
commit 617f614d50
6 changed files with 569 additions and 36 deletions

View File

@@ -1857,18 +1857,15 @@ run_operation_before (FlatpakTransactionOperation *op,
}
static gboolean
add_related (FlatpakTransaction *self,
FlatpakTransactionOperation *op,
GError **error)
op_get_related (FlatpakTransaction *self,
FlatpakTransactionOperation *op,
GPtrArray **out_related,
GError **error)
{
FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
g_autoptr(FlatpakRemoteState) state = NULL;
g_autoptr(GPtrArray) related = NULL;
g_autoptr(GError) local_error = NULL;
int i;
if (priv->disable_related)
return TRUE;
g_autoptr(GError) related_error = NULL;
if (op->kind != FLATPAK_TRANSACTION_OPERATION_UNINSTALL)
{
@@ -1885,15 +1882,37 @@ add_related (FlatpakTransaction *self,
if (transaction_is_local_only (self, op->kind))
related = flatpak_dir_find_local_related_for_metadata (priv->dir, op->ref, op->remote, op->resolved_metakey,
NULL, &local_error);
NULL, &related_error);
else
related = flatpak_dir_find_remote_related_for_metadata (priv->dir, state, op->ref, op->resolved_metakey,
NULL, &local_error);
NULL, &related_error);
if (related_error != NULL)
g_message (_("Warning: Problem looking for related refs: %s"), related_error->message);
if (out_related)
*out_related = g_steal_pointer (&related);
return TRUE;
}
static gboolean
add_related (FlatpakTransaction *self,
FlatpakTransactionOperation *op,
GError **error)
{
FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
g_autoptr(GPtrArray) related = NULL;
int i;
if (priv->disable_related)
return TRUE;
if (!op_get_related (self, op, &related, error))
return FALSE;
if (related == NULL)
{
g_message (_("Warning: Problem looking for related refs: %s"), local_error->message);
return TRUE;
}
return TRUE;
if (op->kind == FLATPAK_TRANSACTION_OPERATION_UNINSTALL)
{
@@ -1985,20 +2004,13 @@ find_runtime_remote (FlatpakTransaction *self,
return NULL;
}
static gboolean
add_deps (FlatpakTransaction *self,
FlatpakTransactionOperation *op,
GError **error)
static char *
op_get_runtime_ref (FlatpakTransactionOperation *op)
{
FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
g_autofree char *runtime_ref = NULL;
g_autofree char *full_runtime_ref = NULL;
g_autofree char *runtime_remote = NULL;
FlatpakTransactionOperation *runtime_op = NULL;
if (!op->resolved_metakey)
return TRUE;
return NULL;
/* Generally only app needs runtimes dependencies, not dependencies because you don't run extensions directly.
However if the extension has extra data (and doesn't define NoRuntime) its also needed so we can run the
@@ -2010,9 +2022,27 @@ add_deps (FlatpakTransaction *self,
runtime_ref = g_key_file_get_string (op->resolved_metakey, "ExtensionOf", "runtime", NULL);
if (runtime_ref == NULL)
return NULL;
return g_strconcat ("runtime/", runtime_ref, NULL);
}
static gboolean
add_deps (FlatpakTransaction *self,
FlatpakTransactionOperation *op,
GError **error)
{
FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
g_autofree char *full_runtime_ref = NULL;
g_autofree char *runtime_remote = NULL;
FlatpakTransactionOperation *runtime_op = NULL;
if (!op->resolved_metakey)
return TRUE;
full_runtime_ref = g_strconcat ("runtime/", runtime_ref, NULL);
full_runtime_ref = op_get_runtime_ref (op);
if (full_runtime_ref == NULL)
return TRUE;
runtime_op = flatpak_transaction_get_last_op_for_ref (self, full_runtime_ref);
@@ -2545,7 +2575,7 @@ emit_op_done (FlatpakTransaction *self,
}
static GBytes *
load_deployed_metadata (FlatpakTransaction *self, const char *ref, char **out_commit)
load_deployed_metadata (FlatpakTransaction *self, const char *ref, char **out_commit, char **out_remote)
{
FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
g_autoptr(GFile) deploy_dir = NULL;
@@ -2557,14 +2587,17 @@ load_deployed_metadata (FlatpakTransaction *self, const char *ref, char **out_co
if (deploy_dir == NULL)
return NULL;
if (out_commit)
if (out_commit || out_remote)
{
g_autoptr(GBytes) deploy_data = NULL;
deploy_data = flatpak_load_deploy_data (deploy_dir, ref, FLATPAK_DEPLOY_VERSION_ANY, NULL, NULL);
if (deploy_data == NULL)
return NULL;
*out_commit = g_strdup (flatpak_deploy_data_get_commit (deploy_data));
if (out_commit)
*out_commit = g_strdup (flatpak_deploy_data_get_commit (deploy_data));
if (out_remote)
*out_remote = g_strdup (flatpak_deploy_data_get_origin (deploy_data));
}
metadata_file = g_file_get_child (deploy_dir, "metadata");
@@ -2649,7 +2682,7 @@ resolve_op_end (FlatpakTransaction *self,
{
g_autoptr(GBytes) old_metadata_bytes = NULL;
old_metadata_bytes = load_deployed_metadata (self, op->ref, NULL);
old_metadata_bytes = load_deployed_metadata (self, op->ref, NULL, NULL);
mark_op_resolved (op, checksum, sideload_path, metadata_bytes, old_metadata_bytes);
emit_eol_and_maybe_skip (self, op);
}
@@ -2781,7 +2814,7 @@ resolve_ops (FlatpakTransaction *self,
{
/* We resolve to the deployed metadata, because we need it to uninstall related ops */
metadata_bytes = load_deployed_metadata (self, op->ref, &checksum);
metadata_bytes = load_deployed_metadata (self, op->ref, &checksum, NULL);
mark_op_resolved (op, checksum, NULL, metadata_bytes, NULL);
continue;
}
@@ -4081,6 +4114,325 @@ flatpak_transaction_normalize_ops (FlatpakTransaction *self)
}
}
static GPtrArray *
find_related_from_deploy (FlatpakTransaction *self,
const char *ref,
char **out_remote)
{
FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
g_autoptr(GBytes) metadata_bytes = NULL;
g_autoptr(GKeyFile) metakey = NULL;
g_autofree char *checksum = NULL;
g_autofree char *remote = NULL;
g_autoptr(GError) related_error = NULL;
g_autoptr(GPtrArray) related = NULL;
metadata_bytes = load_deployed_metadata (self, ref, &checksum, &remote);
if (metadata_bytes == NULL)
return NULL;
if (out_remote)
*out_remote = g_strdup (remote);
metakey = g_key_file_new ();
if (!g_key_file_load_from_bytes (metakey, metadata_bytes, G_KEY_FILE_NONE, NULL))
{
g_message ("Warning: Failed to parse metadata for %s\n", ref);
return NULL;
}
related = flatpak_dir_find_local_related_for_metadata (priv->dir, ref,
remote, metakey,
NULL, &related_error);
if (related_error != NULL)
g_message (_("Warning: Problem looking for related refs: %s"), related_error->message);
return g_steal_pointer (&related);
}
static gboolean
prune_maybe_unused_list (FlatpakTransaction *self,
GHashTable *maybe_unused_runtimes,
GHashTable *metadata_injection,
GPtrArray *to_be_excluded,
GCancellable *cancellable,
GError **error)
{
FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
g_auto(GStrv) unused_refs = NULL;
const char * const *to_be_excluded_strv = NULL;
if (to_be_excluded->len > 0)
{
g_ptr_array_add (to_be_excluded, NULL);
to_be_excluded_strv = (const char * const *) to_be_excluded->pdata;
}
unused_refs = flatpak_dir_list_unused_refs_with_options (priv->dir,
NULL, /* arch */
metadata_injection,
to_be_excluded_strv,
TRUE, /* filter_by_eol */
cancellable, error);
if (unused_refs == NULL)
return FALSE;
GLNX_HASH_TABLE_FOREACH_IT (maybe_unused_runtimes, hashiter, const char *, runtime, const char *, remote)
{
if (!g_strv_contains ((const gchar * const *)unused_refs, runtime))
g_hash_table_iter_remove (&hashiter);
}
return TRUE;
}
static gboolean
populate_maybe_unused_list (FlatpakTransaction *self,
GHashTable *maybe_unused_runtimes,
GCancellable *cancellable,
GError **error)
{
FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
g_auto(GStrv) unused_refs = NULL;
unused_refs = flatpak_dir_list_unused_refs_with_options (priv->dir,
NULL, /* arch */
NULL, /* metadata_injection */
NULL, /* refs_to_exclude */
TRUE, /* filter_by_eol */
cancellable, error);
if (unused_refs == NULL)
return FALSE;
for (char **iter = unused_refs; iter && *iter; iter++)
g_hash_table_replace (maybe_unused_runtimes, g_strdup (*iter), NULL);
return TRUE;
}
static gboolean
add_uninstall_unused_ops (FlatpakTransaction *self,
GCancellable *cancellable,
GError **error)
{
FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
g_autoptr(GHashTable) maybe_unused_runtimes = NULL;
g_autoptr(GHashTable) newly_used_runtimes = NULL;
g_autoptr(GHashTable) metadata_injection = NULL;
g_autoptr(GPtrArray) to_be_excluded = NULL;
g_autoptr(GPtrArray) run_after_ops = NULL;
GList *l, *next;
int i;
if (priv->disable_deps)
return TRUE;
/* This is the set of runtimes which are no longer needed by something in the
* transaction (an uninstall or update). The values are either the relevant
* remote name or %NULL. */
maybe_unused_runtimes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
/* This is the set of runtimes being used by an install or update operation
* in the transaction. */
newly_used_runtimes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
/* This is a mapping from refs to #GKeyFile metadata objects, for each ref
* being installed or updated by the transaction. This will allows us to
* calculate what dependencies will be used after those operations are
* executed. For example an app update may drop an extension point and
* thereby make an installed extension become unused. */
metadata_injection = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
/* This is the set of runtimes and apps scheduled for uninstallation and
* which are therefore excluded when calculating used refs. */
to_be_excluded = g_ptr_array_new ();
/* These are the set of operations which may need to be executed before the
* uninstall operations added by this function. */
run_after_ops = g_ptr_array_new ();
for (l = priv->ops; l != NULL; l = next)
{
FlatpakTransactionOperation *op = l->data;
FlatpakTransactionOperationType op_type = flatpak_transaction_operation_get_operation_type (op);
g_autofree char *runtime_ref = NULL;
g_autoptr(GBytes) deploy_data = NULL;
next = l->next;
if (op->skip)
continue;
g_assert (op_type == FLATPAK_TRANSACTION_OPERATION_UNINSTALL ||
op_type == FLATPAK_TRANSACTION_OPERATION_INSTALL ||
op_type == FLATPAK_TRANSACTION_OPERATION_INSTALL_BUNDLE ||
op_type == FLATPAK_TRANSACTION_OPERATION_UPDATE);
if (op_type == FLATPAK_TRANSACTION_OPERATION_UNINSTALL)
g_ptr_array_add (to_be_excluded, op->ref);
else if (op->resolved_metakey)
g_hash_table_insert (metadata_injection, op->ref, op->resolved_metakey);
if ((op_type == FLATPAK_TRANSACTION_OPERATION_INSTALL ||
op_type == FLATPAK_TRANSACTION_OPERATION_INSTALL_BUNDLE) &&
g_str_has_prefix (op->ref, "runtime/"))
g_hash_table_add (newly_used_runtimes, g_strdup (op->ref));
if ((op_type == FLATPAK_TRANSACTION_OPERATION_INSTALL ||
op_type == FLATPAK_TRANSACTION_OPERATION_INSTALL_BUNDLE) &&
!priv->disable_related)
{
g_autoptr(GPtrArray) related = NULL;
if (!op_get_related (self, op, &related, error))
return FALSE;
for (i = 0; related && i < related->len; i++)
{
FlatpakRelated *rel = g_ptr_array_index (related, i);
if (rel->delete)
g_hash_table_add (newly_used_runtimes, g_strdup (rel->ref));
}
}
runtime_ref = op_get_runtime_ref (op);
if (runtime_ref != NULL)
{
g_autoptr(GPtrArray) runtime_related = NULL;
g_autofree char *runtime_remote = NULL;
if (op_type == FLATPAK_TRANSACTION_OPERATION_UNINSTALL)
{
runtime_related = find_related_from_deploy (self, runtime_ref, &runtime_remote);
}
else
{
FlatpakTransactionOperation *runtime_op = NULL;
runtime_op = flatpak_transaction_get_last_op_for_ref (self, runtime_ref);
if (runtime_op != NULL)
{
runtime_remote = g_strdup (runtime_op->remote);
if (!priv->disable_related && !op_get_related (self, runtime_op, &runtime_related, error))
return FALSE;
}
else
runtime_related = find_related_from_deploy (self, runtime_ref, &runtime_remote);
}
if (op_type == FLATPAK_TRANSACTION_OPERATION_UNINSTALL)
{
g_hash_table_replace (maybe_unused_runtimes, g_strdup (runtime_ref), g_strdup (runtime_remote));
g_ptr_array_add (run_after_ops, op);
}
else
g_hash_table_add (newly_used_runtimes, g_strdup (runtime_ref));
if (!priv->disable_related)
{
for (i = 0; runtime_related && i < runtime_related->len; i++)
{
FlatpakRelated *rel = g_ptr_array_index (runtime_related, i);
if (op_type == FLATPAK_TRANSACTION_OPERATION_UNINSTALL)
{
if (rel->delete)
g_hash_table_replace (maybe_unused_runtimes, g_strdup (rel->ref), g_strdup (runtime_remote));
}
else
g_hash_table_add (newly_used_runtimes, g_strdup (rel->ref));
}
}
}
if (op_type == FLATPAK_TRANSACTION_OPERATION_UPDATE &&
dir_ref_is_installed (priv->dir, op->ref, NULL, &deploy_data))
{
g_autofree char *full_previous_runtime = NULL;
g_autofree char *prev_runtime_remote = NULL;
g_autoptr(GPtrArray) runtime_related = NULL;
g_autoptr(GPtrArray) ref_related = NULL;
g_ptr_array_add (run_after_ops, op);
/* The related refs of the op might now be unused */
if (!priv->disable_related)
{
ref_related = find_related_from_deploy (self, op->ref, NULL);
for (i = 0; ref_related && i < ref_related->len; i++)
{
FlatpakRelated *rel = g_ptr_array_index (ref_related, i);
if (rel->delete)
g_hash_table_replace (maybe_unused_runtimes, g_strdup (rel->ref), g_strdup (op->remote));
}
}
/* If the op is changing to a different runtime, the previous one and
* its related refs might now be unused */
const char *previous_runtime = flatpak_deploy_data_get_runtime (deploy_data);
if (previous_runtime == NULL || *previous_runtime == '\0')
continue;
full_previous_runtime = g_strconcat ("runtime/", previous_runtime, NULL);
if (g_strcmp0 (full_previous_runtime, runtime_ref) == 0)
continue;
runtime_related = find_related_from_deploy (self, full_previous_runtime, &prev_runtime_remote);
g_hash_table_replace (maybe_unused_runtimes,
g_strdup (full_previous_runtime),
g_strdup (prev_runtime_remote));
if (priv->disable_related)
continue;
for (i = 0; runtime_related && i < runtime_related->len; i++)
{
FlatpakRelated *rel = g_ptr_array_index (runtime_related, i);
if (rel->delete)
g_hash_table_replace (maybe_unused_runtimes, g_strdup (rel->ref), g_strdup (prev_runtime_remote));
}
}
}
/* Subtract newly_used_runtimes from maybe_unused_runtimes */
GLNX_HASH_TABLE_FOREACH (newly_used_runtimes, const char *, runtime_ref)
g_hash_table_remove (maybe_unused_runtimes, runtime_ref);
if (g_hash_table_size (maybe_unused_runtimes) == 0)
return TRUE;
/* Check which things in maybe_unused_runtimes will be unused after the
* ops in the transaction are executed. Note that
* maybe_unused_runtimes and to_be_excluded are modified in this helper */
if (!prune_maybe_unused_list (self, maybe_unused_runtimes, metadata_injection, to_be_excluded,
cancellable, error))
return FALSE;
/* Schedule each unused runtime to be uninstalled */
GLNX_HASH_TABLE_FOREACH_KV (maybe_unused_runtimes, const char *, runtime_ref, const char *, remote)
{
g_autofree char *resolved_remote = NULL;
FlatpakTransactionOperation *unused_op = NULL;
if (remote == NULL)
g_assert (dir_ref_is_installed (priv->dir, runtime_ref, &resolved_remote, NULL));
else
resolved_remote = g_strdup (remote);
unused_op = flatpak_transaction_add_op (self, resolved_remote, runtime_ref,
NULL, NULL, NULL, NULL,
FLATPAK_TRANSACTION_OPERATION_UNINSTALL);
for (i = 0; i < run_after_ops->len; i++)
{
FlatpakTransactionOperation *op = g_ptr_array_index (run_after_ops, i);
run_operation_before (op, unused_op, 1);
}
}
return TRUE;
}
static gboolean
flatpak_transaction_real_run (FlatpakTransaction *self,
GCancellable *cancellable,
@@ -4191,11 +4543,20 @@ flatpak_transaction_real_run (FlatpakTransaction *self,
return FALSE;
}
sort_ops (self);
/* Ensure the operation kind is normalized and not no-op */
flatpak_transaction_normalize_ops (self);
/* Add uninstall ops for things that are made unused by this transaction (and
* which match a heuristic). We don't need to do another round of
* resolve_all_ops() since uninstalls don't require that. */
if (!add_uninstall_unused_ops (self, cancellable, error))
{
g_assert (error == NULL || *error != NULL);
return FALSE;
}
sort_ops (self);
g_signal_emit (self, signals[READY], 0, &ready_res);
if (!ready_res)
return flatpak_fail_error (error, FLATPAK_ERROR_ABORTED, _("Aborted by user"));

View File

@@ -322,6 +322,33 @@ g_key_file_load_from_bytes (GKeyFile *key_file,
}
#endif
#if !GLIB_CHECK_VERSION (2, 54, 0)
static inline gboolean
g_ptr_array_find_with_equal_func (GPtrArray *haystack,
gconstpointer needle,
GEqualFunc equal_func,
guint *index_)
{
guint i;
g_return_val_if_fail (haystack != NULL, FALSE);
if (equal_func == NULL)
equal_func = g_direct_equal;
for (i = 0; i < haystack->len; i++)
{
if (equal_func (g_ptr_array_index (haystack, i), needle))
{
if (index_ != NULL)
*index_ = i;
return TRUE;
}
}
return FALSE;
}
#endif
#if !GLIB_CHECK_VERSION (2, 56, 0)
GDateTime *flatpak_g_date_time_new_from_iso8601 (const gchar *text,

View File

@@ -273,7 +273,9 @@ make_runtime () {
GPGARGS="$4"
RUNTIME_REF="runtime/org.test.Platform/$(flatpak --default-arch)/${BRANCH}"
if [ -f ${test_builddir}/runtime-repo/${RUNTIME_REF} ]; then
if [ ! -z "${SRC_RUNTIME_REPO:-}" ]; then
RUNTIME_REPO=repos/${SRC_RUNTIME_REPO}
elif [ -f ${test_builddir}/runtime-repo/${RUNTIME_REF} ]; then
RUNTIME_REPO=${test_builddir}/runtime-repo
else
RUNTIME_REPO=${TEST_DATA_DIR}/runtime-repo
@@ -379,6 +381,20 @@ make_updated_app () {
update_repo $REPONAME "${COLLECTION_ID}"
}
make_updated_runtime () {
REPONAME=${1:-test}
if [ x${USE_COLLECTIONS_IN_SERVER-} == xyes ] ; then
COLLECTION_ID=${2:-org.test.Collection.${REPONAME}}
else
COLLECTION_ID=""
fi
BRANCH=${3:-master}
TEXT=${4:-UPDATED}
GPGARGS="${GPGARGS:-${FL_GPGARGS}}" $(dirname $0)/make-test-runtime.sh repos/${REPONAME} org.test.Platform "${BRANCH}" "${COLLECTION_ID}" "${TEXT}" > /dev/null
update_repo $REPONAME "${COLLECTION_ID}"
}
setup_sdk_repo () {
REPONAME=${1:-test}
if [ x${USE_COLLECTIONS_IN_SERVER-} == xyes ] ; then

View File

@@ -97,5 +97,5 @@ else
fi
mkdir -p repos
flatpak build-export ${collection_args} --disable-sandbox --runtime ${GPGARGS-} ${REPO} ${DIR} ${BRANCH}
flatpak build-export ${collection_args} --disable-sandbox --runtime ${GPGARGS-} ${EXPORT_ARGS-} ${REPO} ${DIR} ${BRANCH}
rm -rf ${DIR}

View File

@@ -24,7 +24,7 @@ set -euo pipefail
skip_without_bwrap
skip_revokefs_without_fuse
echo "1..38"
echo "1..40"
#Regular repo
setup_repo
@@ -424,11 +424,57 @@ ${FLATPAK} ${U} uninstall -y org.test.NewHello org.test.Platform
ok "eol-rebase"
# Remove any pin of the runtime from an earlier test
${FLATPAK} ${U} pin --remove runtime/org.test.Platform/$ARCH/master 2>/dev/null || true
EXPORT_ARGS="--end-of-life=Reason3" make_updated_runtime
${FLATPAK} ${U} install -y test-repo org.test.Hello
${FLATPAK} ${U} list -d > list-log
assert_file_has_content list-log "org\.test\.Hello"
assert_file_has_content list-log "org\.test\.Platform"
${FLATPAK} ${U} uninstall -y org.test.Hello
${FLATPAK} ${U} list -d -a > list-log
assert_not_file_has_content list-log "org\.test\.Hello"
assert_not_file_has_content list-log "org\.test\.Platform"
assert_not_file_has_content list-log "org\.test\.Platform.Locale"
ok "eol runtime uninstalled with app"
${FLATPAK} ${U} install -y test-repo org.test.Hello
${FLATPAK} ${U} info org.test.Platform > info-log
assert_file_has_content info-log "End-of-life: Reason3"
assert_has_dir $FL_DIR/runtime/org.test.Platform/$ARCH/master/active/files
# Update the app to a different runtime branch
make_updated_runtime "" "" "mainline" ""
make_updated_app "" "" "" "UPDATED99" "" "mainline"
${FLATPAK} ${U} update -y org.test.Hello
# The previous runtime should have been removed during the update
assert_has_dir $FL_DIR/runtime/org.test.Platform/$ARCH/mainline/active/files
assert_not_has_dir $FL_DIR/runtime/org.test.Platform/$ARCH/master/active/files
# Revert things for future tests
EXPORT_ARGS="" make_updated_runtime
make_updated_app "" "" "" "UPDATED100" "" "master"
${FLATPAK} ${U} uninstall -y --all
ostree refs --repo=repos/test --delete runtime/org.test.Platform/$ARCH/mainline
ostree refs --repo=repos/test --delete runtime/org.test.Platform.Locale/$ARCH/mainline
update_repo
ok "eol runtime uninstalled on app update to different runtime"
${FLATPAK} ${U} install -y test-repo org.test.Platform
port=$(cat httpd-port)
UPDATE_REPO_ARGS="--redirect-url=http://127.0.0.1:${port}/test-gpg3 --gpg-import=${FL_GPG_HOMEDIR2}/pubring.gpg" update_repo
GPGPUBKEY="${FL_GPG_HOMEDIR2}/pubring.gpg" GPGARGS="${FL_GPGARGS2}" setup_repo_no_add test-gpg3 org.test.Collection.test master
SRC_RUNTIME_REPO="test" GPGPUBKEY="${FL_GPG_HOMEDIR2}/pubring.gpg" GPGARGS="${FL_GPGARGS2}" setup_repo_no_add test-gpg3 org.test.Collection.test master
${FLATPAK} ${U} update -y org.test.Platform
# Ensure we have the new uri

View File

@@ -4334,6 +4334,88 @@ test_installation_unused_refs_excludes_pins (void)
g_assert_cmpint (refs->len, ==, 0);
}
static void
test_installation_unused_refs_across_installations (void)
{
g_autoptr(FlatpakInstallation) system_inst = NULL;
g_autoptr(FlatpakInstallation) user_inst = NULL;
g_autoptr(FlatpakTransaction) transaction = NULL;
g_autoptr(GPtrArray) refs = NULL;
g_autoptr(GError) error = NULL;
g_autofree char *runtime = NULL;
g_autofree char *app = NULL;
FlatpakInstalledRef *unused_ref;
gboolean res;
runtime = g_strdup_printf ("runtime/org.test.Platform/%s/master",
flatpak_get_default_arch ());
app = g_strdup_printf ("app/org.test.Hello/%s/master",
flatpak_get_default_arch ());
system_inst = flatpak_installation_new_system (NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (system_inst);
user_inst = flatpak_installation_new_user (NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (user_inst);
empty_installation (system_inst);
empty_installation (user_inst);
add_remote_system ("test-runtime-only", NULL);
transaction = flatpak_transaction_new_for_installation (system_inst, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (transaction);
res = flatpak_transaction_add_install (transaction, "test-runtime-only-repo", runtime, NULL, &error);
g_assert_no_error (error);
g_assert_true (res);
res = flatpak_transaction_run (transaction, NULL, &error);
g_assert_no_error (error);
g_assert_true (res);
g_clear_object (&transaction);
/* Undo the pinning that happened as a side effect of the install */
const char *argv[] = { "flatpak", "pin", "--system", "--remove", runtime, NULL };
run_test_subprocess ((char **) argv, RUN_TEST_SUBPROCESS_DEFAULT);
flatpak_installation_drop_caches (system_inst, NULL, &error);
g_assert_no_error (error);
/* The runtime should show as unused */
refs = flatpak_installation_list_unused_refs (system_inst, NULL, NULL, &error);
g_assert_nonnull (refs);
g_assert_no_error (error);
g_assert_cmpint (refs->len, ==, 1);
unused_ref = g_ptr_array_index (refs, 0);
g_assert_cmpstr (flatpak_ref_get_name (FLATPAK_REF (unused_ref)), ==, "org.test.Platform");
g_clear_pointer (&refs, g_ptr_array_unref);
/* Install an app in the user installation that uses the runtime in the
* system installation */
transaction = flatpak_transaction_new_for_installation (user_inst, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (transaction);
flatpak_transaction_add_dependency_source (transaction, system_inst);
res = flatpak_transaction_add_install (transaction, repo_name, app, NULL, &error);
g_assert_no_error (error);
g_assert_true (res);
res = flatpak_transaction_run (transaction, NULL, &error);
g_assert_no_error (error);
g_assert_true (res);
/* Now the runtime should be used */
refs = flatpak_installation_list_unused_refs (system_inst, NULL, NULL, &error);
g_assert_nonnull (refs);
g_assert_no_error (error);
g_assert_cmpint (refs->len, ==, 0);
}
int
main (int argc, char *argv[])
{
@@ -4385,6 +4467,7 @@ main (int argc, char *argv[])
g_test_add_func ("/library/installation-no-interaction", test_installation_no_interaction);
g_test_add_func ("/library/installation-unused-refs", test_installation_unused_refs);
g_test_add_func ("/library/installation-unused-refs-excludes-pins", test_installation_unused_refs_excludes_pins);
g_test_add_func ("/library/installation-unused-refs-across-installations", test_installation_unused_refs_across_installations);
global_setup ();