Improve the uninstall commands

Undeploy all deployed commits, remove the ref from the repo,
and prune unowned objects from the repository.
This commit is contained in:
Matthias Clasen
2015-01-16 18:33:46 -05:00
parent 2eed118510
commit c92a2635a8
3 changed files with 220 additions and 90 deletions

View File

@@ -33,7 +33,7 @@
<command>xdg-app uninstall-app</command>
<arg choice="opt" rep="repeat">OPTION</arg>
<arg choice="plain">APP</arg>
<arg choice="opt">ARCH <arg choice="opt">BRANCH</arg></arg>
<arg choice="plain">BRANCH</arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -41,10 +41,16 @@
<title>Description</title>
<para>
Uninstalls an application. <arg choice="plain">APP</arg> must
name an existing application. If <arg choice="plain">ARCH</arg>
or <arg choice="plain">BRANCH</arg> are specified, only the
matching branches of the application are uninstalled.
Uninstalls an application. The <arg choice="plain">APP</arg>,
<arg choice="plain">ARCH</arg> and <arg choice="plain">BRANCH</arg>
arguments must specify an installed application.
</para>
<para>
Normally, this command removes the ref for this application from the
local OSTree repository and purges and objects that are no longer
needed to free up disk space. If the same application is later
reinstalled, the objects will be pulled from the remote repository
again. The --keep-ref option can be used to prevent this.
</para>
<para>
If all branches of the application are removed, this command
@@ -68,6 +74,15 @@
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--keep-ref</option></term>
<listitem><para>
Keep the ref for the application and the objects belonging to it
in the local repository.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--user</option></term>
@@ -77,6 +92,14 @@
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--arch=ARCH</option></term>
<listitem><para>
The architecture to uninstall.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-v</option></term>
<term><option>--verbose</option></term>

View File

@@ -33,7 +33,7 @@
<command>xdg-app uninstall-runtime</command>
<arg choice="opt" rep="repeat">OPTION</arg>
<arg choice="plain">RUNTIME</arg>
<arg choice="opt">ARCH <arg choice="opt">BRANCH</arg></arg>
<arg choice="plain">BRANCH</arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -41,10 +41,16 @@
<title>Description</title>
<para>
Uninstalls a runtime. <arg choice="plain">RUNTIME</arg> must
name an existing runtime. If <arg choice="plain">ARCH</arg>
or <arg choice="plain">BRANCH</arg> are specified, only the
matching branches of the runtime are uninstalled.
Uninstalls a runtime. The <arg choice="plain">RUNTIME</arg>,
<arg choice="plain">ARCH</arg> and <arg choice="plain">BRANCH</arg>
arguments must specify an installed runtime.
</para>
<para>
Normally, this command removes the ref for this runtime from the
local OSTree repository and purges and objects that are no longer
needed to free up disk space. If the same runtime is later reinstalled,
the objects will be pulled from the remote repository again. The
--keep-ref option can be used to prevent this.
</para>
</refsect1>
@@ -64,6 +70,15 @@
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--keep-ref</option></term>
<listitem><para>
Keep the ref for the runtime and the objects belonging to it
in the local repository.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--user</option></term>
@@ -73,6 +88,14 @@
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--arch=ARCH</option></term>
<listitem><para>
The architecture to uninstall.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-v</option></term>
<term><option>--verbose</option></term>

View File

@@ -10,10 +10,45 @@
#include "xdg-app-builtins.h"
#include "xdg-app-utils.h"
static char *opt_arch;
static gboolean opt_keep_ref;
static GOptionEntry options[] = {
{ "arch", 0, 0, G_OPTION_ARG_STRING, &opt_arch, "Arch to uninstall", "ARCH" },
{ "keep-ref", 0, 0, G_OPTION_ARG_NONE, &opt_keep_ref, "Keep ref in local repository", NULL },
{ NULL }
};
static gboolean
single_child_directory (GFile *dir, const char *name, GCancellable *cancellable)
{
gboolean ret = FALSE;
gs_unref_object GFileEnumerator *dir_enum = NULL;
gs_unref_object GFileInfo *child_info = NULL;
dir_enum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable, NULL);
if (!dir_enum)
goto out;
while ((child_info = g_file_enumerator_next_file (dir_enum, cancellable, NULL)))
{
if (strcmp (name, g_file_info_get_name (child_info)) == 0)
{
g_clear_object (&child_info);
continue;
}
goto out;
}
ret = TRUE;
out:
return ret;
}
gboolean
xdg_app_builtin_uninstall_runtime (int argc, char **argv, GCancellable *cancellable, GError **error)
{
@@ -21,31 +56,40 @@ xdg_app_builtin_uninstall_runtime (int argc, char **argv, GCancellable *cancella
GOptionContext *context;
gs_unref_object XdgAppDir *dir = NULL;
gs_unref_object GFile *deploy_base = NULL;
const char *runtime;
const char *arch = NULL;
const char *branch = NULL;
gs_unref_object GFile *arch_dir = NULL;
gs_unref_object GFile *top_dir = NULL;
gs_unref_object GFile *origin = NULL;
gs_unref_object OstreeRepo *repo = NULL;
const char *name;
const char *arch;
const char *branch;
gs_free char *ref = NULL;
gs_free char *repository = NULL;
gs_strfreev char **deployed = NULL;
int i;
GError *temp_error = NULL;
context = g_option_context_new ("RUNTIME [ARCH [BRANCH]] - Uninstall a runtime");
context = g_option_context_new ("RUNTIME BRANCH - Uninstall a runtime");
if (!xdg_app_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error))
goto out;
if (argc < 2)
if (argc < 3)
{
usage_error (context, "RUNTIME must be specified", error);
usage_error (context, "RUNTIME and BRANCH must be specified", error);
goto out;
}
runtime = argv[1];
if (argc >= 3)
arch = argv[2];
if (argc >= 4)
branch = argv[3];
name = argv[1];
branch = argv[2];
if (opt_arch)
arch = opt_arch;
else
arch = xdg_app_get_arch ();
/* TODO: look for apps, require --force */
ref = g_build_filename ("runtime", runtime, arch, branch, NULL);
ref = g_build_filename ("runtime", name, arch, branch, NULL);
deploy_base = xdg_app_dir_get_deploy_dir (dir, ref);
if (!g_file_query_exists (deploy_base, cancellable))
@@ -54,32 +98,62 @@ xdg_app_builtin_uninstall_runtime (int argc, char **argv, GCancellable *cancella
goto out;
}
origin = g_file_get_child (deploy_base, "origin");
if (!g_file_load_contents (origin, cancellable, &repository, NULL, NULL, error))
goto out;
g_debug ("dropping active ref");
if (!xdg_app_dir_set_active (dir, ref, NULL, cancellable, error))
goto out;
if (!xdg_app_dir_list_deployed (dir, ref, &deployed, cancellable, error))
goto out;
for (i = 0; deployed[i] != NULL; i++)
{
g_debug ("undeploying %s", deployed[i]);
if (!xdg_app_dir_undeploy (dir, ref, deployed[i], cancellable, error))
goto out;
}
g_debug ("removing deploy base");
if (!gs_shutil_rm_rf (deploy_base, cancellable, error))
goto out;
if (branch)
g_debug ("cleaning up empty directories");
arch_dir = g_file_get_parent (deploy_base);
if (!g_file_delete (arch_dir, cancellable, &temp_error))
{
gs_unref_object GFile *db;
db = deploy_base;
deploy_base = g_file_get_parent (deploy_base);
if (!g_file_delete (deploy_base, cancellable, NULL))
goto done;
if (!g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY))
{
g_propagate_error (error, temp_error);
goto out;
}
g_clear_error (&temp_error);
}
if (arch)
top_dir = g_file_get_parent (arch_dir);
if (!g_file_delete (top_dir, cancellable, &temp_error))
{
gs_unref_object GFile *db;
db = deploy_base;
deploy_base = g_file_get_parent (deploy_base);
if (!g_file_delete (deploy_base, cancellable, NULL))
goto done;
if (!g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY))
{
g_propagate_error (error, temp_error);
goto out;
}
g_clear_error (&temp_error);
}
if (!opt_keep_ref)
{
repo = xdg_app_dir_get_repo (dir);
if (!ostree_repo_set_ref_immediate (repo, repository, ref, NULL, cancellable, error))
goto out;
if (!xdg_app_dir_prune (dir, cancellable, error))
goto out;
}
done:
ret = TRUE;
out:
@@ -95,30 +169,38 @@ xdg_app_builtin_uninstall_app (int argc, char **argv, GCancellable *cancellable,
GOptionContext *context;
gs_unref_object XdgAppDir *dir = NULL;
gs_unref_object GFile *deploy_base = NULL;
const char *app;
const char *arch = NULL;
const char *branch = NULL;
gs_unref_object GFile *arch_dir = NULL;
gs_unref_object GFile *top_dir = NULL;
gs_unref_object GFile *origin = NULL;
gs_unref_object OstreeRepo *repo = NULL;
const char *name;
const char *arch;
const char *branch;
gs_free char *ref = NULL;
gs_free char *repository = NULL;
gs_strfreev char **deployed = NULL;
int i;
GError *temp_error = NULL;
context = g_option_context_new ("APP [ARCH [BRANCH]] - Uninstall an application");
context = g_option_context_new ("APP BRANCH - Uninstall an application");
if (!xdg_app_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error))
goto out;
if (argc < 2)
if (argc < 3)
{
usage_error (context, "APP must be specified", error);
usage_error (context, "APP and BRANCH must be specified", error);
goto out;
}
app = argv[1];
if (argc >= 3)
arch = argv[2];
if (argc >= 4)
branch = argv[3];
name = argv[1];
branch = argv[2];
if (opt_arch)
arch = opt_arch;
else
arch = xdg_app_get_arch ();
ref = g_build_filename ("app", app, arch, branch, NULL);
ref = g_build_filename ("app", name, arch, branch, NULL);
deploy_base = xdg_app_dir_get_deploy_dir (dir, ref);
if (!g_file_query_exists (deploy_base, cancellable))
@@ -127,59 +209,61 @@ xdg_app_builtin_uninstall_app (int argc, char **argv, GCancellable *cancellable,
goto out;
}
origin = g_file_get_child (deploy_base, "origin");
if (!g_file_load_contents (origin, cancellable, &repository, NULL, NULL, error))
goto out;
g_debug ("dropping active ref");
if (!xdg_app_dir_set_active (dir, ref, NULL, cancellable, error))
goto out;
if (!xdg_app_dir_list_deployed (dir, ref, &deployed, cancellable, error))
goto out;
for (i = 0; deployed[i] != NULL; i++)
{
g_debug ("undeploying %s", deployed[i]);
if (!xdg_app_dir_undeploy (dir, ref, deployed[i], cancellable, error))
goto out;
}
g_debug ("removing deploy base");
if (!gs_shutil_rm_rf (deploy_base, cancellable, error))
goto out;
if (branch)
g_debug ("cleaning up empty directories");
arch_dir = g_file_get_parent (deploy_base);
if (!g_file_delete (arch_dir, cancellable, &temp_error))
{
gs_unref_object GFile *db;
db = deploy_base;
deploy_base = g_file_get_parent (deploy_base);
if (!g_file_delete (deploy_base, cancellable, NULL))
goto done;
}
if (arch)
{
gs_unref_object GFile *db;
gs_unref_object GFileEnumerator *dir_enum;
gs_unref_object GFileInfo *child_info;
db = deploy_base;
deploy_base = g_file_get_parent (deploy_base);
dir_enum = g_file_enumerate_children (deploy_base, G_FILE_ATTRIBUTE_STANDARD_NAME,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable, error);
if (!dir_enum)
goto out;
while ((child_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error)))
if (!g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY))
{
const char *arch;
arch = g_file_info_get_name (child_info);
if (strcmp (arch, "data") == 0)
continue;
goto done;
g_propagate_error (error, temp_error);
goto out;
}
if (temp_error != NULL)
g_clear_error (&temp_error);
}
top_dir = g_file_get_parent (arch_dir);
if (single_child_directory (top_dir, "data", cancellable))
{
if (!gs_shutil_rm_rf (top_dir, cancellable, error))
goto out;
}
if (!opt_keep_ref)
{
repo = xdg_app_dir_get_repo (dir);
if (!ostree_repo_set_ref_immediate (repo, repository, ref, NULL, cancellable, error))
goto out;
if (!gs_shutil_rm_rf (deploy_base, cancellable, error))
if (!xdg_app_dir_prune (dir, cancellable, error))
goto out;
}
done:
ret = TRUE;
out:
if (temp_error != NULL)
g_propagate_error (error, temp_error);
if (context)
g_option_context_free (context);
return ret;