Add support for subpath limited installs

This allows you to install e.g. org.freedesktop.Platform.Locale
but only the /sv subdir, and replaces using separate branches for
each locale.
tingping/wmclass
Alexander Larsson 2016-04-07 20:35:32 +02:00
parent 545a699698
commit 3cc45ccf13
10 changed files with 306 additions and 33 deletions

View File

@ -36,6 +36,7 @@
static char *opt_arch;
static char **opt_gpg_file;
static char **opt_subpaths;
static gboolean opt_no_pull;
static gboolean opt_no_deploy;
static gboolean opt_runtime;
@ -50,6 +51,7 @@ static GOptionEntry options[] = {
{ "app", 0, 0, G_OPTION_ARG_NONE, &opt_app, "Look for app with the specified name", },
{ "bundle", 0, 0, G_OPTION_ARG_NONE, &opt_bundle, "Install from local bundle file", },
{ "gpg-file", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_gpg_file, "Check bundle signatures with GPG key from FILE (- for stdin)", "FILE" },
{ "subpath", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_subpaths, "Only install this subpath", "path" },
{ NULL }
};
@ -286,7 +288,7 @@ xdg_app_builtin_install (int argc, char **argv, GCancellable *cancellable, GErro
if (!opt_no_pull)
{
if (!xdg_app_dir_pull (dir, repository, ref, NULL,
if (!xdg_app_dir_pull (dir, repository, ref, opt_subpaths, NULL,
cancellable, error))
return FALSE;
}
@ -306,6 +308,10 @@ xdg_app_builtin_install (int argc, char **argv, GCancellable *cancellable, GErro
if (!xdg_app_dir_set_origin (dir, ref, repository, cancellable, error))
goto out;
if (!xdg_app_dir_set_subpaths (dir, ref, (const char **)opt_subpaths,
cancellable, error))
goto out;
if (!xdg_app_dir_deploy (dir, ref, NULL, cancellable, error))
goto out;

View File

@ -33,6 +33,7 @@
static char *opt_arch;
static char *opt_commit;
static char **opt_subpaths;
static gboolean opt_force_remove;
static gboolean opt_no_pull;
static gboolean opt_no_deploy;
@ -49,6 +50,7 @@ static GOptionEntry options[] = {
{ "runtime", 0, 0, G_OPTION_ARG_NONE, &opt_runtime, "Look for runtime with the specified name", },
{ "app", 0, 0, G_OPTION_ARG_NONE, &opt_app, "Look for app with the specified name", },
{ "appstream", 0, 0, G_OPTION_ARG_NONE, &opt_appstream, "Update appstream for remote", },
{ "subpath", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_subpaths, "Only update this subpath", "path" },
{ NULL }
};
@ -75,6 +77,7 @@ do_update (XdgAppDir* dir,
{
g_autofree char *ref = NULL;
g_autofree char *repository = NULL;
g_auto(GStrv) subpaths = NULL;
gboolean was_updated = FALSE;
gboolean is_app;
g_auto(GLnxLockFile) lock = GLNX_LOCK_FILE_INIT;
@ -92,10 +95,14 @@ do_update (XdgAppDir* dir,
if (repository == NULL)
return FALSE;
subpaths = xdg_app_dir_get_subpaths (dir, ref, cancellable, error);
if (subpaths == NULL)
return FALSE;
if (!opt_no_pull)
{
if (!xdg_app_dir_pull (dir, repository, ref, NULL,
cancellable, error))
if (!xdg_app_dir_pull (dir, repository, ref, opt_subpaths ? opt_subpaths : subpaths,
NULL, cancellable, error))
return FALSE;
}

View File

@ -521,6 +521,80 @@ xdg_app_dir_set_origin (XdgAppDir *self,
return TRUE;
}
char **
xdg_app_dir_get_subpaths (XdgAppDir *self,
const char *ref,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GFile) deploy_base = NULL;
g_autoptr(GFile) file = NULL;
g_autofree char *data = NULL;
g_autoptr(GError) my_error = NULL;
g_autoptr(GPtrArray) subpaths = NULL;
g_auto(GStrv) lines = NULL;
int i;
deploy_base = xdg_app_dir_get_deploy_dir (self, ref);
if (!g_file_query_exists (deploy_base, cancellable))
{
xdg_app_fail (error, "%s is not installed", ref);
return NULL;
}
file = g_file_get_child (deploy_base, "subpaths");
if (!g_file_load_contents (file, cancellable, &data, NULL, NULL, &my_error))
{
if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
data = g_strdup ("");
else
g_propagate_error (error, g_steal_pointer (&my_error));
return NULL;
}
lines = g_strsplit (data, "\n", 0);
subpaths = g_ptr_array_new ();
for (i = 0; lines[i] != NULL; i++)
{
lines[i] = g_strstrip (lines[i]);
if (lines[i][0] == '/')
g_ptr_array_add (subpaths, g_strdup (lines[i]));
}
g_ptr_array_add (subpaths, NULL);
return (char **)g_ptr_array_free (subpaths, FALSE);
}
gboolean
xdg_app_dir_set_subpaths (XdgAppDir *self,
const char *ref,
const char **subpaths,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GFile) deploy_base = NULL;
g_autoptr(GFile) file = NULL;
g_autofree char *data = NULL;
deploy_base = xdg_app_dir_get_deploy_dir (self, ref);
if (!g_file_query_exists (deploy_base, cancellable))
{
xdg_app_fail (error, "%s is not installed", ref);
return FALSE;
}
data = g_strjoinv ("\n", (char **)subpaths);
file = g_file_get_child (deploy_base, "subpaths");
if (!g_file_replace_contents (file, data, strlen (data), NULL, FALSE,
G_FILE_CREATE_NONE, NULL, cancellable, error))
return FALSE;
return TRUE;
}
gboolean
xdg_app_dir_ensure_path (XdgAppDir *self,
GCancellable *cancellable,
@ -690,7 +764,7 @@ xdg_app_dir_update_appstream (XdgAppDir *self,
if (!ostree_repo_resolve_rev (self->repo, remote_and_branch, TRUE, &old_checksum, error))
return FALSE;
if (!xdg_app_dir_pull (self, remote, branch, progress,
if (!xdg_app_dir_pull (self, remote, branch, NULL, progress,
cancellable, error))
return FALSE;
@ -785,10 +859,45 @@ xdg_app_dir_update_appstream (XdgAppDir *self,
return TRUE;
}
/* This is a copy of ostree_repo_pull_one_dir that always disables
static deltas if subdir is used */
static gboolean
repo_pull_one_dir (OstreeRepo *self,
const char *remote_name,
const char *dir_to_pull,
char **refs_to_fetch,
OstreeRepoPullFlags flags,
OstreeAsyncProgress *progress,
GCancellable *cancellable,
GError **error)
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
if (dir_to_pull)
{
g_variant_builder_add (&builder, "{s@v}", "subdir",
g_variant_new_variant (g_variant_new_string (dir_to_pull)));
g_variant_builder_add (&builder, "{s@v}", "disable-static-deltas",
g_variant_new_variant (g_variant_new_boolean (TRUE)));
}
g_variant_builder_add (&builder, "{s@v}", "flags",
g_variant_new_variant (g_variant_new_int32 (flags)));
if (refs_to_fetch)
g_variant_builder_add (&builder, "{s@v}", "refs",
g_variant_new_variant (g_variant_new_strv ((const char *const*) refs_to_fetch, -1)));
return ostree_repo_pull_with_options (self, remote_name, g_variant_builder_end (&builder),
progress, cancellable, error);
}
gboolean
xdg_app_dir_pull (XdgAppDir *self,
const char *repository,
const char *ref,
char **subpaths,
OstreeAsyncProgress *progress,
GCancellable *cancellable,
GError **error)
@ -825,13 +934,46 @@ xdg_app_dir_pull (XdgAppDir *self,
refs[0] = ref;
refs[1] = NULL;
if (!ostree_repo_pull (self->repo, repository,
(char **)refs, OSTREE_REPO_PULL_FLAGS_NONE,
progress,
cancellable, error))
if (subpaths == NULL || subpaths[0] == NULL)
{
g_prefix_error (error, "While pulling %s from remote %s: ", ref, repository);
goto out;
if (!ostree_repo_pull (self->repo, repository,
(char **)refs, OSTREE_REPO_PULL_FLAGS_NONE,
progress,
cancellable, error))
{
g_prefix_error (error, "While pulling %s from remote %s: ", ref, repository);
goto out;
}
}
else
{
int i;
if (!repo_pull_one_dir (self->repo, repository,
"/metadata",
(char **)refs, OSTREE_REPO_PULL_FLAGS_NONE,
progress,
cancellable, error))
{
g_prefix_error (error, "While pulling %s from remote %s, metadata: ",
ref, repository);
goto out;
}
for (i = 0; subpaths[i] != NULL; i++)
{
g_autofree char *subpath = g_build_filename ("/files", subpaths[i], NULL);
if (!repo_pull_one_dir (self->repo, repository,
subpath,
(char **)refs, OSTREE_REPO_PULL_FLAGS_NONE,
progress,
cancellable, error))
{
g_prefix_error (error, "While pulling %s from remote %s, subpath %s: ",
ref, repository, subpaths[i]);
goto out;
}
}
}
ret = TRUE;
@ -1916,6 +2058,7 @@ xdg_app_dir_deploy (XdgAppDir *self,
g_autoptr(GFile) export = NULL;
g_autoptr(OstreeAsyncProgress) progress = NULL;
g_autoptr(GKeyFile) keyfile = NULL;
g_auto(GStrv) subpaths = NULL;
if (!xdg_app_dir_ensure_repo (self, cancellable, error))
return FALSE;
@ -1969,20 +2112,73 @@ xdg_app_dir_deploy (XdgAppDir *self,
if (file_info == NULL)
return FALSE;
if (!ostree_repo_checkout_tree (self->repo,
OSTREE_REPO_CHECKOUT_MODE_USER,
OSTREE_REPO_CHECKOUT_OVERWRITE_NONE,
checkoutdir,
OSTREE_REPO_FILE (root), file_info,
cancellable, error))
{
g_autofree char *rootpath = NULL;
g_autofree char *checkoutpath = NULL;
subpaths = xdg_app_dir_get_subpaths (self, ref, cancellable, error);
if (subpaths == NULL)
return FALSE;
rootpath = g_file_get_path (root);
checkoutpath = g_file_get_path (checkoutdir);
g_prefix_error (error, "While trying to checkout %s into %s: ", rootpath, checkoutpath);
return FALSE;
if (*subpaths == NULL)
{
if (!ostree_repo_checkout_tree (self->repo,
OSTREE_REPO_CHECKOUT_MODE_USER,
OSTREE_REPO_CHECKOUT_OVERWRITE_NONE,
checkoutdir,
OSTREE_REPO_FILE (root), file_info,
cancellable, error))
{
g_autofree char *rootpath = NULL;
g_autofree char *checkoutpath = NULL;
rootpath = g_file_get_path (root);
checkoutpath = g_file_get_path (checkoutdir);
g_prefix_error (error, "While trying to checkout %s into %s: ", rootpath, checkoutpath);
return FALSE;
}
}
else
{
OstreeRepoCheckoutOptions options = { 0, };
g_autofree char *checkoutdirpath = g_file_get_path (checkoutdir);
g_autoptr(GFile) files = g_file_get_child (checkoutdir, "files");
int i;
if (!g_file_make_directory_with_parents (files, cancellable, error))
return FALSE;
options.mode = OSTREE_REPO_CHECKOUT_MODE_USER;
options.overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES;
options.subpath = "/metadata";
access ("checkout metadata", 0);
if (!ostree_repo_checkout_tree_at (self->repo, &options,
AT_FDCWD, checkoutdirpath,
checksum,
cancellable, error))
{
g_prefix_error (error, "While trying to checkout metadata subpath: ");
return FALSE;
}
for (i = 0; subpaths[i] != NULL; i++)
{
g_autofree char *subpath = g_build_filename ("/files", subpaths[i], NULL);
g_autofree char *dstpath = g_build_filename (checkoutdirpath, "/files", subpaths[i], NULL);
g_autofree char *dstpath_parent = g_path_get_dirname (dstpath);
if (g_mkdir_with_parents (dstpath_parent, 0755))
{
glnx_set_error_from_errno (error);
return FALSE;
}
options.subpath = subpath;
if (!ostree_repo_checkout_tree_at (self->repo, &options,
AT_FDCWD, dstpath,
checksum,
cancellable, error))
{
g_prefix_error (error, "While trying to checkout metadata subpath: ");
return FALSE;
}
}
}
dotref = g_file_resolve_relative_path (checkoutdir, "files/.ref");

View File

@ -88,6 +88,15 @@ gboolean xdg_app_dir_set_origin (XdgAppDir *self,
const char *remote,
GCancellable *cancellable,
GError **error);
char ** xdg_app_dir_get_subpaths (XdgAppDir *self,
const char *ref,
GCancellable *cancellable,
GError **error);
gboolean xdg_app_dir_set_subpaths (XdgAppDir *self,
const char *ref,
const char **subpaths,
GCancellable *cancellable,
GError **error);
GFile * xdg_app_dir_get_exports_dir (XdgAppDir *self);
GFile * xdg_app_dir_get_removed_dir (XdgAppDir *self);
GFile * xdg_app_dir_get_if_deployed (XdgAppDir *self,
@ -144,6 +153,7 @@ gboolean xdg_app_dir_update_appstream(XdgAppDir *self,
gboolean xdg_app_dir_pull (XdgAppDir *self,
const char *repository,
const char *ref,
char **subpaths,
OstreeAsyncProgress *progress,
GCancellable *cancellable,
GError **error);

View File

@ -104,6 +104,15 @@
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--subpath=PATH</option></term>
<listitem><para>
Install only a subpath of the ref. This is mainly used to install a subset of locales.
This can be added multiple times to install multiple subpaths.,
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--no-deploy</option></term>

View File

@ -111,6 +111,16 @@
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--subpath=PATH</option></term>
<listitem><para>
Install only a subpath of the ref. This is mainly used to install a subset of locales.
This can be added multiple times to install multiple subpaths.
If this is not specified the subpaths specified at install time are reused.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--commit=COMMIT</option></term>

View File

@ -290,6 +290,7 @@ get_ref (XdgAppInstallation *self,
g_autoptr(GFile) deploy_subdir = NULL;
g_autofree char *deploy_path = NULL;
g_autofree char *latest_commit = NULL;
g_auto(GStrv) subpaths = NULL;
gboolean is_current = FALSE;
guint64 installed_size = 0;
@ -297,6 +298,8 @@ get_ref (XdgAppInstallation *self,
origin = xdg_app_dir_get_origin (priv->dir, full_ref, NULL, NULL);
commit = xdg_app_dir_read_active (priv->dir, full_ref, cancellable);
subpaths = xdg_app_dir_get_subpaths (priv->dir, full_ref, cancellable, NULL);
deploy_dir = xdg_app_dir_get_deploy_dir (priv->dir, full_ref);
if (deploy_dir && commit)
{
@ -323,7 +326,7 @@ get_ref (XdgAppInstallation *self,
return xdg_app_installed_ref_new (full_ref,
commit,
latest_commit,
origin,
origin, subpaths,
deploy_path,
installed_size,
is_current);
@ -992,7 +995,7 @@ xdg_app_installation_install (XdgAppInstallation *self,
g_object_set_data (G_OBJECT (ostree_progress), "last_progress", GUINT_TO_POINTER(0));
}
if (!xdg_app_dir_pull (dir_clone, remote_name, ref,
if (!xdg_app_dir_pull (dir_clone, remote_name, ref, NULL,
ostree_progress, cancellable, error))
goto out;
@ -1088,6 +1091,7 @@ xdg_app_installation_update (XdgAppInstallation *self,
XdgAppInstalledRef *result = NULL;
gboolean was_updated = FALSE;
g_auto(GLnxLockFile) lock = GLNX_LOCK_FILE_INIT;
g_auto(GStrv) subpaths = NULL;
ref = xdg_app_compose_ref (kind == XDG_APP_REF_KIND_APP, name, branch, arch, error);
if (ref == NULL)
@ -1106,6 +1110,10 @@ xdg_app_installation_update (XdgAppInstallation *self,
if (remote_name == NULL)
return NULL;
subpaths = xdg_app_dir_get_subpaths (priv->dir, ref, cancellable, error);
if (subpaths == NULL)
return FALSE;
/* Pull, prune, etc are not threadsafe, so we work on a copy */
dir_clone = xdg_app_dir_clone (priv->dir);
@ -1122,7 +1130,7 @@ xdg_app_installation_update (XdgAppInstallation *self,
if ((flags & XDG_APP_UPDATE_FLAGS_NO_PULL) == 0)
{
if (!xdg_app_dir_pull (dir_clone, remote_name, ref,
if (!xdg_app_dir_pull (dir_clone, remote_name, ref, subpaths,
ostree_progress, cancellable, error))
goto out;
}

View File

@ -32,6 +32,7 @@ XdgAppInstalledRef *xdg_app_installed_ref_new (const char *full_ref,
const char *commit,
const char *latest_commit,
const char *origin,
char **subpaths,
const char *deploy_dir,
guint64 installed_size,
gboolean current);

View File

@ -44,6 +44,7 @@ struct _XdgAppInstalledRefPrivate
char *origin;
char *latest_commit;
char *deploy_dir;
char **subpaths;
guint64 installed_size;
};
@ -56,7 +57,8 @@ enum {
PROP_ORIGIN,
PROP_LATEST_COMMIT,
PROP_DEPLOY_DIR,
PROP_INSTALLED_SIZE
PROP_INSTALLED_SIZE,
PROP_SUBPATHS
};
static void
@ -67,6 +69,7 @@ xdg_app_installed_ref_finalize (GObject *object)
g_free (priv->origin);
g_free (priv->deploy_dir);
g_strfreev (priv->subpaths);
G_OBJECT_CLASS (xdg_app_installed_ref_parent_class)->finalize (object);
}
@ -105,6 +108,11 @@ xdg_app_installed_ref_set_property (GObject *object,
priv->deploy_dir = g_value_dup_string (value);
break;
case PROP_SUBPATHS:
g_clear_pointer (&priv->subpaths, g_strfreev);
priv->subpaths = g_strdupv (g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -142,6 +150,10 @@ xdg_app_installed_ref_get_property (GObject *object,
g_value_set_string (value, priv->deploy_dir);
break;
case PROP_SUBPATHS:
g_value_set_boxed (value, priv->subpaths);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -192,6 +204,13 @@ xdg_app_installed_ref_class_init (XdgAppInstalledRefClass *klass)
"Where the application is installed",
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_SUBPATHS,
g_param_spec_boxed ("subpaths",
"",
"",
G_TYPE_STRV,
G_PARAM_READWRITE));
}
static void
@ -319,6 +338,7 @@ xdg_app_installed_ref_new (const char *full_ref,
const char *commit,
const char *latest_commit,
const char *origin,
char **subpaths,
const char *deploy_dir,
guint64 installed_size,
gboolean is_current)
@ -332,6 +352,10 @@ xdg_app_installed_ref_new (const char *full_ref,
if (strcmp (parts[0], "app") != 0)
kind = XDG_APP_REF_KIND_RUNTIME;
/* Canonicalize the "no subpaths" case */
if (subpaths && *subpaths == NULL)
subpaths = NULL;
ref = g_object_new (XDG_APP_TYPE_INSTALLED_REF,
"kind", kind,
"name", parts[1],
@ -340,6 +364,7 @@ xdg_app_installed_ref_new (const char *full_ref,
"commit", commit,
"latest-commit", latest_commit,
"origin", origin,
"subpaths", subpaths,
"is-current", is_current,
"installed-size", installed_size,
"deploy-dir", deploy_dir,

View File

@ -48,12 +48,13 @@ typedef struct {
G_DEFINE_AUTOPTR_CLEANUP_FUNC(XdgAppInstalledRef, g_object_unref)
#endif
XDG_APP_EXTERN const char *xdg_app_installed_ref_get_origin (XdgAppInstalledRef *self);
XDG_APP_EXTERN guint64 xdg_app_installed_ref_get_installed_size (XdgAppInstalledRef *self);
XDG_APP_EXTERN const char *xdg_app_installed_ref_get_deploy_dir (XdgAppInstalledRef *self);
XDG_APP_EXTERN const char *xdg_app_installed_ref_get_latest_commit (XdgAppInstalledRef *self);
XDG_APP_EXTERN gboolean xdg_app_installed_ref_get_is_current (XdgAppInstalledRef *self);
XDG_APP_EXTERN GBytes *xdg_app_installed_ref_load_metadata (XdgAppInstalledRef *self,
XDG_APP_EXTERN const char *xdg_app_installed_ref_get_origin (XdgAppInstalledRef *self);
XDG_APP_EXTERN const char **xdg_app_installed_ref_get_subpaths (XdgAppInstalledRef *self);
XDG_APP_EXTERN guint64 xdg_app_installed_ref_get_installed_size (XdgAppInstalledRef *self);
XDG_APP_EXTERN const char *xdg_app_installed_ref_get_deploy_dir (XdgAppInstalledRef *self);
XDG_APP_EXTERN const char *xdg_app_installed_ref_get_latest_commit (XdgAppInstalledRef *self);
XDG_APP_EXTERN gboolean xdg_app_installed_ref_get_is_current (XdgAppInstalledRef *self);
XDG_APP_EXTERN GBytes *xdg_app_installed_ref_load_metadata (XdgAppInstalledRef *self,
GCancellable *cancellable,
GError **error);