extensions-tool: Add 'install' command
The ability to install unaudited extensions directly from a zip file can be useful for testing and code review, so implement a corresponding command that complements the previously added 'pack' command. https://gitlab.gnome.org/GNOME/gnome-shell/issues/1234
This commit is contained in:
parent
f9357457bf
commit
a429fdbd08
207
src/extensions-tool/command-install.c
Normal file
207
src/extensions-tool/command-install.c
Normal file
@ -0,0 +1,207 @@
|
||||
/* command-install.c
|
||||
*
|
||||
* Copyright 2018 Florian Müllner <fmuellner@gnome.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include <gnome-autoar/gnome-autoar.h>
|
||||
#include <json-glib/json-glib.h>
|
||||
|
||||
#include "commands.h"
|
||||
#include "common.h"
|
||||
#include "config.h"
|
||||
|
||||
static JsonObject *
|
||||
load_metadata (GFile *dir,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr (JsonParser) parser = NULL;
|
||||
g_autoptr (GInputStream) stream = NULL;
|
||||
g_autoptr (GFile) file = NULL;
|
||||
|
||||
file = g_file_get_child (dir, "metadata.json");
|
||||
stream = G_INPUT_STREAM (g_file_read (file, NULL, error));
|
||||
if (stream == NULL)
|
||||
return NULL;
|
||||
|
||||
parser = json_parser_new_immutable ();
|
||||
if (!json_parser_load_from_stream (parser, stream, NULL, error))
|
||||
return NULL;
|
||||
|
||||
return json_node_dup_object (json_parser_get_root (parser));
|
||||
}
|
||||
|
||||
static void
|
||||
on_error (AutoarExtractor *extractor,
|
||||
GError *error,
|
||||
gpointer data)
|
||||
{
|
||||
*((GError **)data) = g_error_copy (error);
|
||||
}
|
||||
|
||||
static GFile *
|
||||
on_decide_destination (AutoarExtractor *extractor,
|
||||
GFile *dest,
|
||||
GList *files,
|
||||
gpointer data)
|
||||
{
|
||||
g_autofree char *dest_path = NULL;
|
||||
GFile *new_dest;
|
||||
int copy = 1;
|
||||
|
||||
dest_path = g_file_get_path (dest);
|
||||
new_dest = g_object_ref (dest);
|
||||
|
||||
while (g_file_query_exists (new_dest, NULL))
|
||||
{
|
||||
g_autofree char *new_path = g_strdup_printf ("%s (%d)", dest_path, copy);
|
||||
|
||||
g_object_unref (new_dest);
|
||||
new_dest = g_file_new_for_path (new_path);
|
||||
|
||||
copy++;
|
||||
}
|
||||
|
||||
*((GFile **)data) = g_object_ref (new_dest);
|
||||
|
||||
return new_dest;
|
||||
}
|
||||
|
||||
static int
|
||||
install_extension (const char *bundle,
|
||||
gboolean force)
|
||||
{
|
||||
g_autoptr (AutoarExtractor) extractor = NULL;
|
||||
g_autoptr (JsonObject) metadata = NULL;
|
||||
g_autoptr (GFile) cachedir = NULL;
|
||||
g_autoptr (GFile) tmpdir = NULL;
|
||||
g_autoptr (GFile) src = NULL;
|
||||
g_autoptr (GFile) dst = NULL;
|
||||
g_autoptr (GFile) dstdir = NULL;
|
||||
g_autoptr (GError) error = NULL;
|
||||
g_autofree char *cwd = NULL;
|
||||
const char *uuid;
|
||||
|
||||
cwd = g_get_current_dir ();
|
||||
src = g_file_new_for_commandline_arg_and_cwd (bundle, cwd);
|
||||
cachedir = g_file_new_for_path (g_get_user_cache_dir ());
|
||||
|
||||
extractor = autoar_extractor_new (src, cachedir);
|
||||
|
||||
g_signal_connect (extractor, "error", G_CALLBACK (on_error), &error);
|
||||
g_signal_connect (extractor, "decide-destination", G_CALLBACK (on_decide_destination), &tmpdir);
|
||||
|
||||
autoar_extractor_start (extractor, NULL);
|
||||
|
||||
if (error != NULL)
|
||||
goto err;
|
||||
|
||||
metadata = load_metadata (tmpdir, &error);
|
||||
if (metadata == NULL)
|
||||
goto err;
|
||||
|
||||
dstdir = g_file_new_build_filename (g_get_user_data_dir (),
|
||||
"gnome-shell", "extensions", NULL);
|
||||
|
||||
if (!g_file_make_directory_with_parents (dstdir, NULL, &error))
|
||||
goto err;
|
||||
|
||||
uuid = json_object_get_string_member (metadata, "uuid");
|
||||
dst = g_file_get_child (dstdir, uuid);
|
||||
|
||||
if (g_file_query_exists (dst, NULL))
|
||||
{
|
||||
if (!force)
|
||||
{
|
||||
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_EXISTS,
|
||||
"%s exists and --force was not specified", uuid);
|
||||
goto err;
|
||||
}
|
||||
else if (!file_delete_recursively (dst, &error))
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!g_file_move (tmpdir, dst, G_FILE_COPY_NONE, NULL, NULL, NULL, &error))
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (error != NULL)
|
||||
g_printerr ("%s\n", error->message);
|
||||
|
||||
if (tmpdir != NULL)
|
||||
file_delete_recursively (tmpdir, NULL);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
int
|
||||
handle_install (int argc, char *argv[], gboolean do_help)
|
||||
{
|
||||
g_autoptr (GOptionContext) context = NULL;
|
||||
g_autoptr (GError) error = NULL;
|
||||
g_auto (GStrv) filenames = NULL;
|
||||
gboolean force = FALSE;
|
||||
GOptionEntry entries[] = {
|
||||
{ .long_name = "force", .short_name = 'f',
|
||||
.arg = G_OPTION_ARG_NONE, .arg_data = &force,
|
||||
.description = _("Overwrite an existing extension") },
|
||||
{ .long_name = G_OPTION_REMAINING,
|
||||
.arg_description =_("EXTENSION_BUNDLE"),
|
||||
.arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = &filenames },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
g_set_prgname ("gnome-extensions install");
|
||||
|
||||
context = g_option_context_new (NULL);
|
||||
g_option_context_set_help_enabled (context, FALSE);
|
||||
g_option_context_set_summary (context, _("Install an extension bundle"));
|
||||
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
|
||||
|
||||
if (do_help)
|
||||
{
|
||||
show_help (context, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!g_option_context_parse (context, &argc, &argv, &error))
|
||||
{
|
||||
show_help (context, error->message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (filenames == NULL)
|
||||
{
|
||||
show_help (context, _("No extension bundle specified"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (g_strv_length (filenames) > 1)
|
||||
{
|
||||
show_help (context, _("More than one extension bundle specified"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return install_extension (*filenames, force);
|
||||
}
|
@ -38,28 +38,6 @@ typedef struct _ExtensionPack {
|
||||
static void extension_pack_free (ExtensionPack *);
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ExtensionPack, extension_pack_free);
|
||||
|
||||
static void
|
||||
delete_recursively (GFile *file)
|
||||
{
|
||||
g_autoptr (GFileEnumerator) file_enum = NULL;
|
||||
GFile *child;
|
||||
|
||||
file_enum = g_file_enumerate_children (file, NULL, 0, NULL, NULL);
|
||||
if (file_enum)
|
||||
while (TRUE)
|
||||
{
|
||||
if (!g_file_enumerator_iterate (file_enum, NULL, &child, NULL, NULL))
|
||||
return;
|
||||
|
||||
if (child == NULL)
|
||||
break;
|
||||
|
||||
delete_recursively (child);
|
||||
}
|
||||
|
||||
g_file_delete (file, NULL, NULL);
|
||||
}
|
||||
|
||||
static ExtensionPack *
|
||||
extension_pack_new (const char *srcdir)
|
||||
{
|
||||
@ -74,7 +52,7 @@ static void
|
||||
extension_pack_free (ExtensionPack *pack)
|
||||
{
|
||||
if (pack->tmpdir)
|
||||
delete_recursively (pack->tmpdir);
|
||||
file_delete_recursively (pack->tmpdir, NULL);
|
||||
|
||||
g_clear_pointer (&pack->files, g_hash_table_destroy);
|
||||
g_clear_pointer (&pack->metadata, json_object_unref);
|
||||
|
@ -30,5 +30,6 @@ int handle_list (int argc, char *argv[], gboolean do_help);
|
||||
int handle_info (int argc, char *argv[], gboolean do_help);
|
||||
int handle_create (int argc, char *argv[], gboolean do_help);
|
||||
int handle_pack (int argc, char *argv[], gboolean do_help);
|
||||
int handle_install (int argc, char *argv[], gboolean do_help);
|
||||
|
||||
G_END_DECLS
|
||||
|
@ -54,4 +54,7 @@ void print_extension_info (GVariantDict *info,
|
||||
GDBusProxy *get_shell_proxy (GError **error);
|
||||
GSettings *get_shell_settings (void);
|
||||
|
||||
gboolean file_delete_recursively (GFile *file,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
@ -5,7 +5,7 @@
|
||||
################################################################################
|
||||
|
||||
__gnome_extensions() {
|
||||
local commands="version enable disable info show list create pack"
|
||||
local commands="version enable disable info install show list create pack"
|
||||
local COMMAND=${COMP_WORDS[1]}
|
||||
|
||||
_init_completion -s || return
|
||||
@ -54,6 +54,13 @@ __gnome_extensions() {
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
install)
|
||||
if [[ $cur != -* ]]
|
||||
then
|
||||
_filedir zip
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Stop if we are currently waiting for an option value
|
||||
|
@ -126,6 +126,31 @@ print_extension_info (GVariantDict *info,
|
||||
g_print (" %s: %s\n", _("State"), extension_state_to_string (state));
|
||||
}
|
||||
|
||||
gboolean
|
||||
file_delete_recursively (GFile *file,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr (GFileEnumerator) file_enum = NULL;
|
||||
GFile *child;
|
||||
|
||||
file_enum = g_file_enumerate_children (file, NULL, 0, NULL, NULL);
|
||||
if (file_enum)
|
||||
while (TRUE)
|
||||
{
|
||||
if (!g_file_enumerator_iterate (file_enum, NULL, &child, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
if (child == NULL)
|
||||
break;
|
||||
|
||||
if (!file_delete_recursively (child, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return g_file_delete (file, NULL, error);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
handle_version (int argc, char *argv[], gboolean do_help)
|
||||
{
|
||||
@ -163,6 +188,7 @@ usage (void)
|
||||
g_printerr (" show %s\n", _("Show extension info"));
|
||||
g_printerr (" create %s\n", _("Create extension"));
|
||||
g_printerr (" pack %s\n", _("Package extension"));
|
||||
g_printerr (" install %s\n", _("Install extension bundle"));
|
||||
g_printerr ("\n");
|
||||
g_printerr (_("Use %s to get detailed help.\n"), "“gnome-extensions help COMMAND”");
|
||||
}
|
||||
@ -230,6 +256,8 @@ main (int argc, char *argv[])
|
||||
return handle_create (argc, argv, do_help);
|
||||
else if (g_str_equal (command, "pack"))
|
||||
return handle_pack (argc, argv, do_help);
|
||||
else if (g_str_equal (command, "install"))
|
||||
return handle_install (argc, argv, do_help);
|
||||
else
|
||||
usage ();
|
||||
|
||||
|
@ -29,6 +29,8 @@ SYNOPSIS
|
||||
|
||||
*gnome-extensions* pack ['OPTION'...]
|
||||
|
||||
*gnome-extensions* install ['OPTION'...] 'PACK'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
*gnome-extensions* is a utility that makes some common GNOME extensions
|
||||
@ -147,6 +149,22 @@ the current directory otherwise.
|
||||
*--out-dir*='DIRECTORY':::
|
||||
The directory where the pack should be created
|
||||
|
||||
*install* ['OPTION'...] 'PACK'::
|
||||
Installs an extension from the bundle 'PACK'.
|
||||
+
|
||||
The command unpacks the extension files and moves them to
|
||||
the expected location in the user's *$HOME*, so that it
|
||||
will be loaded in the next session.
|
||||
+
|
||||
It is mainly intended for testing, not as a replacement for
|
||||
GNOME Software or the extension website. As extensions have
|
||||
privileged access to the user's session, it is advised to
|
||||
never load extensions from untrusted sources without carefully
|
||||
reviewing their content.
|
||||
+
|
||||
.Options
|
||||
*--force*:::
|
||||
Override an existing extension
|
||||
|
||||
EXIT STATUS
|
||||
-----------
|
||||
|
@ -4,6 +4,7 @@ sources = [
|
||||
'command-disable.c',
|
||||
'command-enable.c',
|
||||
'command-info.c',
|
||||
'command-install.c',
|
||||
'command-list.c',
|
||||
'command-pack.c',
|
||||
'common.h',
|
||||
|
@ -13,6 +13,7 @@ sources = [
|
||||
'command-disable.c',
|
||||
'command-enable.c',
|
||||
'command-info.c',
|
||||
'command-install.c',
|
||||
'command-list.c',
|
||||
'command-pack.c',
|
||||
'main.c'
|
||||
|
Loading…
Reference in New Issue
Block a user