Fix/server startup config update failure handling (#1646)

* Catch config value migration exception

In case the value did not exist in the config a "ConfigException.Missing" exception was thrown which caused the whole migration to fail.

Fixes #1645

* Improve config migration logging

* Update user config file after config update

The user config file gets reset before the update.
This could cause the user settings to get lost on the next server start in case something went wrong during the update and the updated config never got saved to the actual file.
This commit is contained in:
schroda
2025-09-14 16:32:23 +02:00
committed by GitHub
parent 2818fbe575
commit bbd7e30298
2 changed files with 65 additions and 42 deletions
@@ -19,6 +19,7 @@ import eu.kanade.tachiyomi.App
import eu.kanade.tachiyomi.createAppModule
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.local.LocalSource
import io.github.config4k.getValue
import io.github.config4k.toConfig
import io.github.oshai.kotlinlogging.KotlinLogging
import io.javalin.json.JavalinJackson
@@ -144,7 +145,7 @@ fun setupLogLevelUpdating(
)
}
fun migrateConfig(
fun migrateConfigValue(
configDocument: ConfigDocument,
config: Config,
configKey: String,
@@ -168,6 +169,54 @@ fun migrateConfig(
return configDocument
}
fun migrateConfig(
configDocument: ConfigDocument,
config: Config,
): ConfigDocument {
var updatedConfig = configDocument
val settingsRequiringMigration = SettingsRegistry.getAll().filterValues { it.deprecated?.replaceWith != null }
settingsRequiringMigration.forEach { (name, data) ->
val configKey = "server.$name"
val toConfigKey = "server.${data.deprecated!!.replaceWith}"
try {
config.getValue(configKey)
} catch (_: ConfigException) {
// Ignore, no migration required
return@forEach
}
logger.debug { "Migrating config value: $configKey -> $toConfigKey" }
try {
if (data.deprecated!!.migrateConfig != null) {
updatedConfig = data.deprecated!!.migrateConfig!!(config.getValue(configKey), updatedConfig)
return@forEach
}
if (data.deprecated!!.migrateConfigValue != null) {
updatedConfig =
migrateConfigValue(
updatedConfig,
config,
configKey,
toConfigKey,
data.deprecated!!.migrateConfigValue!!,
)
return@forEach
}
} catch (e: Exception) {
logger.warn(e) { "Failed to migrate config value: $configKey -> $toConfigKey" }
return@forEach
}
shutdownApp(ExitCode.ConfigMigrationMisconfiguredFailure)
}
return updatedConfig
}
fun serverModule(applicationDirs: ApplicationDirs): Module =
module {
single { applicationDirs }
@@ -310,37 +359,7 @@ fun applicationSetup() {
}
} else {
// make sure the user config file is up-to-date
GlobalConfigManager.updateUserConfig { config ->
var updatedConfig = this
val settingsRequiringMigration = SettingsRegistry.getAll().filterValues { it.deprecated?.replaceWith != null }
settingsRequiringMigration.forEach { (name, data) ->
val configKey = "server.$name"
val toConfigKey = "server.${data.deprecated!!.replaceWith}"
if (data.deprecated!!.migrateConfig != null) {
logger.debug { "Migrating config value: $configKey -> $toConfigKey" }
updatedConfig = data.deprecated!!.migrateConfig!!(config.getValue(configKey), updatedConfig)
return@forEach
}
if (data.deprecated!!.migrateConfigValue != null) {
updatedConfig =
migrateConfig(
updatedConfig,
config,
configKey,
toConfigKey,
data.deprecated!!.migrateConfigValue!!,
)
return@forEach
}
shutdownApp(ExitCode.ConfigMigrationMisconfiguredFailure)
}
updatedConfig
}
GlobalConfigManager.updateUserConfig { migrateConfig(this, it) }
}
} catch (e: Exception) {
logger.error(e) { "Exception while creating initial server.conf" }