From 20d932657255b4a9964fa21846e68e59fe692941 Mon Sep 17 00:00:00 2001 From: Vankka Date: Sat, 25 Jan 2025 14:27:41 +0200 Subject: [PATCH] Backup configs automatically upon upgrade --- .../discordsrv/common/AbstractDiscordSRV.java | 16 +++++++++++++--- .../manager/MessagesConfigManager.java | 5 +++-- .../manager/abstraction/ConfigManager.java | 4 +++- .../abstraction/ConfigurateConfigManager.java | 11 ++++++++--- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/common/src/main/java/com/discordsrv/common/AbstractDiscordSRV.java b/common/src/main/java/com/discordsrv/common/AbstractDiscordSRV.java index a4d220c5..312e2e3c 100644 --- a/common/src/main/java/com/discordsrv/common/AbstractDiscordSRV.java +++ b/common/src/main/java/com/discordsrv/common/AbstractDiscordSRV.java @@ -105,9 +105,12 @@ import java.io.InputStream; import java.lang.reflect.Constructor; import java.net.*; +import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; +import java.time.LocalDateTime; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -747,12 +750,19 @@ protected List reload(Set flags, boolean initial) thro } boolean configUpgrade = flags.contains(ReloadFlag.CONFIG_UPGRADE); + Path backupPath = null; + if (configUpgrade) { + String dateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss").format(LocalDateTime.now()); + backupPath = dataDirectory().resolve("config-migrated").resolve(dateAndTime); + Files.createDirectories(backupPath); + } + if (flags.contains(ReloadFlag.CONFIG) || configUpgrade) { try { AtomicBoolean anyMissingOptions = new AtomicBoolean(false); - connectionConfigManager().reload(configUpgrade, anyMissingOptions); - configManager().reload(configUpgrade, anyMissingOptions); - messagesConfigManager().reload(configUpgrade, anyMissingOptions); + connectionConfigManager().reload(configUpgrade, anyMissingOptions, backupPath); + configManager().reload(configUpgrade, anyMissingOptions, backupPath); + messagesConfigManager().reload(configUpgrade, anyMissingOptions, backupPath); if (anyMissingOptions.get()) { logger().info("Use \"/discordsrv reload config_upgrade\" to write the latest configuration"); diff --git a/common/src/main/java/com/discordsrv/common/config/configurate/manager/MessagesConfigManager.java b/common/src/main/java/com/discordsrv/common/config/configurate/manager/MessagesConfigManager.java index 52774de8..211a6105 100644 --- a/common/src/main/java/com/discordsrv/common/config/configurate/manager/MessagesConfigManager.java +++ b/common/src/main/java/com/discordsrv/common/config/configurate/manager/MessagesConfigManager.java @@ -24,6 +24,7 @@ import com.discordsrv.common.core.logging.Logger; import com.discordsrv.common.core.logging.NamedLogger; import com.discordsrv.common.exception.ConfigException; +import org.jetbrains.annotations.Nullable; import java.nio.file.Files; import java.nio.file.Path; @@ -63,7 +64,7 @@ public Path directory() { return discordSRV.dataDirectory().resolve("messages"); } - public void reload(boolean forceSave, AtomicBoolean anyMissingOptions) throws ConfigException { + public void reload(boolean forceSave, AtomicBoolean anyMissingOptions, @Nullable Path backupPath) throws ConfigException { synchronized (configs) { configs.clear(); @@ -112,7 +113,7 @@ public void reload(boolean forceSave, AtomicBoolean anyMissingOptions) throws Co } for (Map.Entry> entry : configs.entrySet()) { - entry.getValue().reload(forceSave, anyMissingOptions); + entry.getValue().reload(forceSave, anyMissingOptions, backupPath); } } } diff --git a/common/src/main/java/com/discordsrv/common/config/configurate/manager/abstraction/ConfigManager.java b/common/src/main/java/com/discordsrv/common/config/configurate/manager/abstraction/ConfigManager.java index 713720a6..372f295c 100644 --- a/common/src/main/java/com/discordsrv/common/config/configurate/manager/abstraction/ConfigManager.java +++ b/common/src/main/java/com/discordsrv/common/config/configurate/manager/abstraction/ConfigManager.java @@ -19,9 +19,11 @@ package com.discordsrv.common.config.configurate.manager.abstraction; import com.discordsrv.common.exception.ConfigException; +import org.jetbrains.annotations.Nullable; import org.spongepowered.configurate.CommentedConfigurationNode; import org.spongepowered.configurate.loader.AbstractConfigurationLoader; +import java.nio.file.Path; import java.util.concurrent.atomic.AtomicBoolean; public interface ConfigManager { @@ -29,6 +31,6 @@ public interface ConfigManager { T createConfiguration(); T config(); - void reload(boolean forceSave, AtomicBoolean anyMissingOptions) throws ConfigException; + void reload(boolean forceSave, AtomicBoolean anyMissingOptions, @Nullable Path backupPath) throws ConfigException; void save(AbstractConfigurationLoader loader) throws ConfigException; } diff --git a/common/src/main/java/com/discordsrv/common/config/configurate/manager/abstraction/ConfigurateConfigManager.java b/common/src/main/java/com/discordsrv/common/config/configurate/manager/abstraction/ConfigurateConfigManager.java index ff62d9c9..126d5501 100644 --- a/common/src/main/java/com/discordsrv/common/config/configurate/manager/abstraction/ConfigurateConfigManager.java +++ b/common/src/main/java/com/discordsrv/common/config/configurate/manager/abstraction/ConfigurateConfigManager.java @@ -55,6 +55,7 @@ import org.spongepowered.configurate.yaml.ScalarStyle; import org.spongepowered.configurate.yaml.YamlConfigurationLoader; +import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.Type; @@ -391,7 +392,7 @@ protected void translate(CommentedConfigurationNode node) throws ConfigurateExce @SuppressWarnings("unchecked") // Cast to generic @Override - public void reload(boolean forceSave, AtomicBoolean anyMissingOptions) throws ConfigException { + public void reload(boolean forceSave, AtomicBoolean anyMissingOptions, @Nullable Path backupPath) throws ConfigException { T defaultConfig = createConfiguration(); Class defaultConfigClass = (Class) defaultConfig.getClass(); @@ -410,6 +411,10 @@ public void reload(boolean forceSave, AtomicBoolean anyMissingOptions) throws Co return; } + if (backupPath != null) { + Files.copy(filePath(), backupPath.resolve(fileName())); + } + // Load existing file & translate CommentedConfigurationNode node = loader().load(); @@ -427,9 +432,9 @@ public void reload(boolean forceSave, AtomicBoolean anyMissingOptions) throws Co if (forceSave) { save(loader); } - } catch (ConfigurateException e) { + } catch (IOException e) { Class configClass = defaultConfig.getClass(); - if (!configClass.isAnnotationPresent(ConfigSerializable.class)) { + if (e instanceof ConfigurateException && !configClass.isAnnotationPresent(ConfigSerializable.class)) { // Not very obvious and can easily happen throw new ConfigException(configClass.getName() + " is not annotated with @ConfigSerializable", e); }