Merge branch 'mjog/data-location-migration' into 'mainline'
Automatic config migration and per-build-profile directories Closes #326 See merge request GNOME/geary!608
This commit is contained in:
commit
3aa9eb77a6
7 changed files with 246 additions and 34 deletions
|
|
@ -25,6 +25,8 @@
|
|||
"--talk-name=org.gnome.evolution.dataserver.Sources5",
|
||||
"--filesystem=xdg-cache/evolution/addressbook:ro",
|
||||
"--metadata=X-DConf=migrate-path=/org/gnome/Geary/",
|
||||
"--filesystem=~/.config/geary:ro",
|
||||
"--filesystem=~/.var/app/org.gnome.Geary/config/geary:ro",
|
||||
"--filesystem=/tmp"
|
||||
],
|
||||
"cleanup": [
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@ finish-args:
|
|||
# Migrate GSettings into the sandbox
|
||||
- "--metadata=X-DConf=migrate-path=/org/gnome/Geary/"
|
||||
|
||||
# Migrate Geary settings from other release versions
|
||||
- "--filesystem=~/.config/geary:ro"
|
||||
- "--filesystem=~/.var/app/org.gnome.Geary/config/geary:ro"
|
||||
|
||||
# Let view source keep on working as-sis for now. Bug 779311. */
|
||||
- "--filesystem=/tmp"
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,11 @@ public class Application.Client : Gtk.Application {
|
|||
public const string SOURCE_ROOT_DIR = _SOURCE_ROOT_DIR;
|
||||
public const string BUILD_ROOT_DIR = _BUILD_ROOT_DIR;
|
||||
|
||||
// keep these in sync with meson_options.txt
|
||||
public const string PROFILE_RELEASE = "release";
|
||||
public const string PROFILE_BETA = "beta";
|
||||
public const string PROFILE_DEVEL = "development";
|
||||
|
||||
public const string[] AUTHORS = {
|
||||
"Jim Nelson <jim@yorba.org>",
|
||||
"Eric Gregory <eric@yorba.org>",
|
||||
|
|
@ -212,6 +217,14 @@ public class Application.Client : Gtk.Application {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if Geary appears to be running under Flatpak.
|
||||
*
|
||||
* If this returns `true`, then the application instance
|
||||
* appears to be running inside a Flatpak sandbox.
|
||||
*/
|
||||
public bool is_flatpak_sandboxed { get; private set; }
|
||||
|
||||
/**
|
||||
* The global controller for this application instance.
|
||||
*
|
||||
|
|
@ -317,6 +330,7 @@ public class Application.Client : Gtk.Application {
|
|||
);
|
||||
this.add_main_option_entries(OPTION_ENTRIES);
|
||||
this.window_removed.connect_after(on_window_removed);
|
||||
this.is_flatpak_sandboxed = GLib.FileUtils.test("/.flatpak-info", EXISTS);
|
||||
}
|
||||
|
||||
public override bool local_command_line(ref unowned string[] args,
|
||||
|
|
@ -759,25 +773,25 @@ public class Application.Client : Gtk.Application {
|
|||
}
|
||||
}
|
||||
|
||||
/** Returns the application's base user configuration directory. */
|
||||
public GLib.File get_user_config_directory() {
|
||||
/** Returns the application's base home configuration directory. */
|
||||
public GLib.File get_home_config_directory() {
|
||||
return GLib.File.new_for_path(
|
||||
Environment.get_user_config_dir()
|
||||
).get_child("geary");
|
||||
).get_child(get_geary_home_dir_name());
|
||||
}
|
||||
|
||||
/** Returns the application's base user cache directory. */
|
||||
public GLib.File get_user_cache_directory() {
|
||||
/** Returns the application's base home cache directory. */
|
||||
public GLib.File get_home_cache_directory() {
|
||||
return GLib.File.new_for_path(
|
||||
GLib.Environment.get_user_cache_dir()
|
||||
).get_child("geary");
|
||||
).get_child(get_geary_home_dir_name());
|
||||
}
|
||||
|
||||
/** Returns the application's base user data directory. */
|
||||
public GLib.File get_user_data_directory() {
|
||||
/** Returns the application's base home data directory. */
|
||||
public GLib.File get_home_data_directory() {
|
||||
return GLib.File.new_for_path(
|
||||
GLib.Environment.get_user_data_dir()
|
||||
).get_child("geary");
|
||||
).get_child(get_geary_home_dir_name());
|
||||
}
|
||||
|
||||
/** Returns the application's base static resources directory. */
|
||||
|
|
@ -858,6 +872,34 @@ public class Application.Client : Gtk.Application {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of paths of possible config locations.
|
||||
*
|
||||
* This is useful only for migrating configuration from
|
||||
* non-Flatpak to Flatpak or release-builds to non-release builds.
|
||||
*/
|
||||
internal GLib.File[] get_config_search_path() {
|
||||
var paths = new GLib.File[] {};
|
||||
var home = GLib.File.new_for_path(GLib.Environment.get_home_dir());
|
||||
paths += home.get_child(
|
||||
".config"
|
||||
).get_child(
|
||||
"geary"
|
||||
);
|
||||
paths += home.get_child(
|
||||
".var"
|
||||
).get_child(
|
||||
"app"
|
||||
).get_child(
|
||||
"org.gnome.Geary"
|
||||
).get_child(
|
||||
"config"
|
||||
).get_child(
|
||||
"geary"
|
||||
);
|
||||
return paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an error notification.
|
||||
*
|
||||
|
|
@ -1146,6 +1188,22 @@ public class Application.Client : Gtk.Application {
|
|||
}
|
||||
}
|
||||
|
||||
private string get_geary_home_dir_name() {
|
||||
// Return the standard name if running a release build or
|
||||
// running under Flatpak, otherwise append the build profile
|
||||
// as a suffix so (e.g.) devel builds don't mess with release
|
||||
// build's config and databases.
|
||||
//
|
||||
// Note that non-release Flatpak builds already have their own
|
||||
// separate directories since they have different app ids, and
|
||||
// hence don't need the suffix.
|
||||
return (
|
||||
_PROFILE == PROFILE_RELEASE || this.is_flatpak_sandboxed
|
||||
? "geary"
|
||||
: "geary-" + _PROFILE
|
||||
);
|
||||
}
|
||||
|
||||
private void on_activate_about() {
|
||||
this.show_about.begin();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ public class Application.Configuration : Geary.BaseObject {
|
|||
settings = new Settings(schema_id);
|
||||
gnome_interface = new Settings("org.gnome.desktop.interface");
|
||||
|
||||
Migrate.old_app_config(settings);
|
||||
Util.Migrate.old_app_config(settings);
|
||||
|
||||
this.bind(SINGLE_KEY_SHORTCUTS, this, SINGLE_KEY_SHORTCUTS);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,6 +125,9 @@ internal class Application.Controller :
|
|||
this.application = application;
|
||||
this.controller_open = cancellable;
|
||||
|
||||
GLib.File config_dir = application.get_home_config_directory();
|
||||
GLib.File data_dir = application.get_home_data_directory();
|
||||
|
||||
// This initializes the IconFactory, important to do before
|
||||
// the actions are created (as they refer to some of Geary's
|
||||
// custom icons)
|
||||
|
|
@ -137,11 +140,11 @@ internal class Application.Controller :
|
|||
Components.WebView.init_web_context(
|
||||
this.application.config,
|
||||
this.application.get_web_extensions_dir(),
|
||||
this.application.get_user_cache_directory().get_child("web-resources")
|
||||
);
|
||||
Components.WebView.load_resources(
|
||||
this.application.get_user_config_directory()
|
||||
this.application.get_home_cache_directory().get_child(
|
||||
"web-resources"
|
||||
)
|
||||
);
|
||||
Components.WebView.load_resources(config_dir);
|
||||
Composer.WebView.load_resources();
|
||||
ConversationWebView.load_resources();
|
||||
Accounts.SignatureWebView.load_resources();
|
||||
|
|
@ -170,14 +173,23 @@ internal class Application.Controller :
|
|||
this.application.get_app_plugins_dir()
|
||||
);
|
||||
|
||||
// Create standard config directory
|
||||
try {
|
||||
config_dir.make_directory_with_parents();
|
||||
} catch (GLib.IOError.EXISTS err) {
|
||||
// fine
|
||||
}
|
||||
|
||||
// Migrate configuration if necessary.
|
||||
Migrate.xdg_config_dir(this.application.get_user_data_directory(),
|
||||
this.application.get_user_config_directory());
|
||||
Util.Migrate.xdg_config_dir(config_dir, data_dir);
|
||||
Util.Migrate.release_config(
|
||||
application.get_config_search_path(), config_dir
|
||||
);
|
||||
|
||||
// Hook up cert, accounts and credentials machinery
|
||||
|
||||
this.certificate_manager = yield new Application.CertificateManager(
|
||||
this.application.get_user_data_directory().get_child("pinned-certs"),
|
||||
config_dir.get_child("pinned-certs"),
|
||||
cancellable
|
||||
);
|
||||
|
||||
|
|
@ -187,8 +199,8 @@ internal class Application.Controller :
|
|||
|
||||
this.account_manager = new Accounts.Manager(
|
||||
libsecret,
|
||||
this.application.get_user_config_directory(),
|
||||
this.application.get_user_data_directory()
|
||||
config_dir,
|
||||
data_dir
|
||||
);
|
||||
this.account_manager.account_added.connect(
|
||||
on_account_added
|
||||
|
|
|
|||
|
|
@ -507,7 +507,7 @@ public class Application.MainWindow :
|
|||
load_config(application.config);
|
||||
restore_saved_window_state();
|
||||
|
||||
if (_PROFILE != "release") {
|
||||
if (_PROFILE != Client.PROFILE_RELEASE) {
|
||||
this.get_style_context().add_class("devel");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
/* Copyright 2016 Software Freedom Conservancy Inc.
|
||||
/*
|
||||
* Copyright © 2016 Software Freedom Conservancy Inc.
|
||||
* Copyright © 2020 Michael Gratton <mike@vee.net>
|
||||
*
|
||||
* This software is licensed under the GNU Lesser General Public License
|
||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
n */
|
||||
|
||||
namespace Migrate {
|
||||
namespace Util.Migrate {
|
||||
private const string GROUP = "AccountInformation";
|
||||
private const string PRIMARY_EMAIL_KEY = "primary_email";
|
||||
private const string SETTINGS_FILENAME = Accounts.Manager.SETTINGS_FILENAME;
|
||||
|
|
@ -19,21 +21,14 @@ namespace Migrate {
|
|||
* It also appends a "primary_email" key to the new configuration file to reliaby keep
|
||||
* track of the user's email address.
|
||||
*/
|
||||
public static void xdg_config_dir(File user_data_dir, File user_config_dir) throws Error {
|
||||
public static void xdg_config_dir(GLib.File user_config_dir,
|
||||
GLib.File user_data_dir)
|
||||
throws GLib.Error {
|
||||
File new_config_dir;
|
||||
File old_data_dir;
|
||||
File new_config_file;
|
||||
File old_config_file;
|
||||
|
||||
// Create ~/.config/geary
|
||||
try {
|
||||
user_config_dir.make_directory_with_parents();
|
||||
} catch (Error err) {
|
||||
// The user may have already created the directory, so don't throw EXISTS.
|
||||
if (!(err is IOError.EXISTS))
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Return if Geary has never been run (~/.local/share/geary does not exist).
|
||||
if (!user_data_dir.query_exists())
|
||||
return;
|
||||
|
|
@ -109,6 +104,145 @@ namespace Migrate {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates configuration from release build locations.
|
||||
*
|
||||
* This will migrate configuration from release build locations to
|
||||
* the current config directory, if and only if the current config
|
||||
* directory is empty. For example, from the standard
|
||||
* distro-package config location to the current Flatpak location,
|
||||
* or from either to a development config location.
|
||||
*/
|
||||
public static void release_config(GLib.File[] search_path,
|
||||
GLib.File config_dir)
|
||||
throws GLib.Error {
|
||||
if (is_directory_empty(config_dir)) {
|
||||
GLib.File? most_recent = null;
|
||||
GLib.DateTime most_recent_modified = null;
|
||||
foreach (var source in search_path) {
|
||||
if (!source.equal(config_dir)) {
|
||||
GLib.DateTime? src_modified = null;
|
||||
try {
|
||||
GLib.FileInfo? src_info = source.query_info(
|
||||
GLib.FileAttribute.TIME_MODIFIED, 0
|
||||
);
|
||||
if (src_info != null) {
|
||||
src_modified =
|
||||
src_info.get_modification_date_time();
|
||||
}
|
||||
} catch (GLib.IOError.NOT_FOUND err) {
|
||||
// fine
|
||||
} catch (GLib.Error err) {
|
||||
debug(
|
||||
"Error querying release config dir %s: %s",
|
||||
source.get_path(),
|
||||
err.message
|
||||
);
|
||||
}
|
||||
if (most_recent_modified == null ||
|
||||
(src_modified != null &&
|
||||
most_recent_modified.compare(src_modified) < 0)) {
|
||||
most_recent = source;
|
||||
most_recent_modified = src_modified;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (most_recent != null) {
|
||||
try {
|
||||
debug(
|
||||
"Migrating release config from %s to %s",
|
||||
most_recent.get_path(),
|
||||
config_dir.get_path()
|
||||
);
|
||||
recursive_copy(most_recent, config_dir);
|
||||
} catch (GLib.Error err) {
|
||||
debug("Error migrating release config: %s", err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool is_directory_empty(GLib.File dir) {
|
||||
bool is_empty = true;
|
||||
GLib.FileEnumerator? existing = null;
|
||||
try {
|
||||
existing = dir.enumerate_children(
|
||||
GLib.FileAttribute.STANDARD_TYPE, 0
|
||||
);
|
||||
} catch (GLib.IOError.NOT_FOUND err) {
|
||||
// fine
|
||||
} catch (GLib.Error err) {
|
||||
debug(
|
||||
"Error enumerating directory %s: %s",
|
||||
dir.get_path(),
|
||||
err.message
|
||||
);
|
||||
}
|
||||
|
||||
if (existing != null) {
|
||||
try {
|
||||
is_empty = existing.next_file() == null;
|
||||
} catch (GLib.Error err) {
|
||||
debug(
|
||||
"Error getting next child in directory %s: %s",
|
||||
dir.get_path(),
|
||||
err.message
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
existing.close();
|
||||
} catch (GLib.Error err) {
|
||||
debug(
|
||||
"Error closing directory enumeration %s: %s",
|
||||
dir.get_path(),
|
||||
err.message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return is_empty;
|
||||
}
|
||||
|
||||
private static void recursive_copy(GLib.File src,
|
||||
GLib.File dest,
|
||||
GLib.Cancellable? cancellable = null
|
||||
) throws GLib.Error {
|
||||
switch (src.query_file_type(NONE, cancellable)) {
|
||||
case DIRECTORY:
|
||||
try {
|
||||
dest.make_directory(cancellable);
|
||||
} catch (GLib.IOError.EXISTS err) {
|
||||
// fine
|
||||
}
|
||||
src.copy_attributes(dest, NONE, cancellable);
|
||||
|
||||
GLib.FileEnumerator children = src.enumerate_children(
|
||||
GLib.FileAttribute.STANDARD_NAME,
|
||||
NONE,
|
||||
cancellable
|
||||
);
|
||||
GLib.FileInfo? child = children.next_file(cancellable);
|
||||
while (child != null) {
|
||||
recursive_copy(
|
||||
src.get_child(child.get_name()),
|
||||
dest.get_child(child.get_name())
|
||||
);
|
||||
child = children.next_file(cancellable);
|
||||
}
|
||||
break;
|
||||
|
||||
case REGULAR:
|
||||
src.copy(dest, NONE, cancellable);
|
||||
break;
|
||||
|
||||
default:
|
||||
// no-op
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public const string OLD_APP_ID = "org.yorba.geary";
|
||||
private const string MIGRATED_CONFIG_KEY = "migrated-config";
|
||||
|
||||
|
|
@ -137,4 +271,6 @@ namespace Migrate {
|
|||
|
||||
newSettings.set_boolean(MIGRATED_CONFIG_KEY, true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue