diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 487918ce..7ee899cb 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -44,11 +44,16 @@ dependencies { // current database driver implementation("com.h2database:h2:1.4.200") + // Exposed Migrations + val exposedMigrationsVersion = "5611dbec5a" + implementation("com.github.Suwayomi:exposed-migrations:$exposedMigrationsVersion") + // tray icon implementation("com.dorkbox:SystemTray:4.1") implementation("com.dorkbox:Utilities:1.9") + // dependencies of Tachiyomi extensions, some are duplicate, keeping it here for reference implementation("com.github.inorichi.injekt:injekt-core:65b0440") implementation("com.squareup.okhttp3:okhttp:4.9.1") diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt index e205e4c4..fc1f9969 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt @@ -13,7 +13,6 @@ import org.kodein.di.DI import org.kodein.di.bind import org.kodein.di.conf.global import org.kodein.di.singleton -import suwayomi.tachidesk.server.BuildConfig import suwayomi.tachidesk.server.database.databaseUp import suwayomi.tachidesk.server.util.AppMutex.handleAppMutex import suwayomi.tachidesk.server.util.SystemTray.systemTray diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBManager.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBManager.kt index 553afa27..a6d84239 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBManager.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBManager.kt @@ -7,13 +7,14 @@ package suwayomi.tachidesk.server.database * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +import de.neonew.exposed.migrations.loadMigrationsFrom +import de.neonew.exposed.migrations.runMigrations import org.jetbrains.exposed.sql.Database import org.kodein.di.DI import org.kodein.di.conf.global import org.kodein.di.instance import suwayomi.tachidesk.server.ApplicationDirs -import suwayomi.tachidesk.server.database.migration.lib.loadMigrationsFrom -import suwayomi.tachidesk.server.database.migration.lib.runMigrations +import suwayomi.tachidesk.server.ServerConfig object DBManager { val db by lazy { @@ -27,6 +28,6 @@ fun databaseUp() { val db = DBManager.db db.useNestedTransactions = true - val migrations = loadMigrationsFrom("suwayomi.tachidesk.server.database.migration") + val migrations = loadMigrationsFrom("suwayomi.tachidesk.server.database.migration", ServerConfig::class.java) runMigrations(migrations) } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0001_Initial.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0001_Initial.kt index 12dc1edc..4e2474a2 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0001_Initial.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0001_Initial.kt @@ -7,12 +7,12 @@ package suwayomi.tachidesk.server.database.migration * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +import de.neonew.exposed.migrations.Migration import eu.kanade.tachiyomi.source.model.SManga import org.jetbrains.exposed.dao.id.IdTable import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.transactions.transaction -import suwayomi.tachidesk.server.database.migration.lib.Migration @Suppress("ClassName", "unused") class M0001_Initial : Migration() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0002_ChapterTableIndexRename.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0002_ChapterTableIndexRename.kt index 582dd9fe..2027bc2e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0002_ChapterTableIndexRename.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0002_ChapterTableIndexRename.kt @@ -7,9 +7,9 @@ package suwayomi.tachidesk.server.database.migration * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +import de.neonew.exposed.migrations.Migration import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.vendors.currentDialect -import suwayomi.tachidesk.server.database.migration.lib.Migration @Suppress("ClassName", "unused") class M0002_ChapterTableIndexRename : Migration() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0003_DefaultCategory.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0003_DefaultCategory.kt index 5a3dc463..a65f6c49 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0003_DefaultCategory.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0003_DefaultCategory.kt @@ -7,9 +7,9 @@ package suwayomi.tachidesk.server.database.migration * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +import de.neonew.exposed.migrations.Migration import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.vendors.currentDialect -import suwayomi.tachidesk.server.database.migration.lib.Migration @Suppress("ClassName", "unused") class M0003_DefaultCategory : Migration() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0004_AnimeTablesBatch1.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0004_AnimeTablesBatch1.kt index 1abee838..0404e4c9 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0004_AnimeTablesBatch1.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0004_AnimeTablesBatch1.kt @@ -1,17 +1,17 @@ package suwayomi.tachidesk.server.database.migration +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import de.neonew.exposed.migrations.Migration import org.jetbrains.exposed.dao.id.IdTable import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.transactions.transaction -import suwayomi.tachidesk.server.database.migration.lib.Migration - -/* - * Copyright (C) Contributors to the Suwayomi project - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ class M0004_AnimeTablesBatch1 : Migration() { private class AnimeExtensionTable : IntIdTable() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0005_AnimeTablesBatch2.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0005_AnimeTablesBatch2.kt index 62fcc1ac..90018c46 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0005_AnimeTablesBatch2.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0005_AnimeTablesBatch2.kt @@ -1,17 +1,17 @@ package suwayomi.tachidesk.server.database.migration +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import de.neonew.exposed.migrations.Migration import eu.kanade.tachiyomi.animesource.model.SAnime import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.transactions.transaction -import suwayomi.tachidesk.server.database.migration.lib.Migration - -/* - * Copyright (C) Contributors to the Suwayomi project - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ class M0005_AnimeTablesBatch2 : Migration() { private class AnimeTable : IntIdTable() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0006_AnimeTablesBatch3.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0006_AnimeTablesBatch3.kt index 17d8c202..8833093d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0006_AnimeTablesBatch3.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0006_AnimeTablesBatch3.kt @@ -1,17 +1,17 @@ package suwayomi.tachidesk.server.database.migration +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import de.neonew.exposed.migrations.Migration import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.transactions.transaction import suwayomi.tachidesk.anime.model.table.AnimeTable -import suwayomi.tachidesk.server.database.migration.lib.Migration - -/* - * Copyright (C) Contributors to the Suwayomi project - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ class M0006_AnimeTablesBatch3 : Migration() { private class EpisodeTable : IntIdTable() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0007_ChapterIsDownloaded.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0007_ChapterIsDownloaded.kt index ef6cd299..5a532411 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0007_ChapterIsDownloaded.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0007_ChapterIsDownloaded.kt @@ -7,9 +7,9 @@ package suwayomi.tachidesk.server.database.migration * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +import de.neonew.exposed.migrations.Migration import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.vendors.currentDialect -import suwayomi.tachidesk.server.database.migration.lib.Migration @Suppress("ClassName", "unused") class M0007_ChapterIsDownloaded : Migration() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0008_ChapterPageCount.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0008_ChapterPageCount.kt index 9b038e21..76fdd2f1 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0008_ChapterPageCount.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0008_ChapterPageCount.kt @@ -7,9 +7,9 @@ package suwayomi.tachidesk.server.database.migration * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +import de.neonew.exposed.migrations.Migration import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.vendors.currentDialect -import suwayomi.tachidesk.server.database.migration.lib.Migration @Suppress("ClassName", "unused") class M0008_ChapterPageCount : Migration() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0009_ChapterLastReadAt.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0009_ChapterLastReadAt.kt index 81a2603a..028391b8 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0009_ChapterLastReadAt.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0009_ChapterLastReadAt.kt @@ -7,9 +7,9 @@ package suwayomi.tachidesk.server.database.migration * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +import de.neonew.exposed.migrations.Migration import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.vendors.currentDialect -import suwayomi.tachidesk.server.database.migration.lib.Migration @Suppress("ClassName", "unused") class M0009_ChapterLastReadAt : Migration() { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0010_MangaAndChapterMeta.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0010_MangaAndChapterMeta.kt index c47e74bf..2a33939a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0010_MangaAndChapterMeta.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0010_MangaAndChapterMeta.kt @@ -1,12 +1,12 @@ package suwayomi.tachidesk.server.database.migration +import de.neonew.exposed.migrations.Migration import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.ReferenceOption import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.transactions.transaction import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.MangaTable -import suwayomi.tachidesk.server.database.migration.lib.Migration /* * Copyright (C) Contributors to the Suwayomi project diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/lib/LICENSE b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/lib/LICENSE deleted file mode 100644 index bb6a9096..00000000 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/lib/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Andreas Mausch - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/lib/Migration.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/lib/Migration.kt deleted file mode 100644 index 6dbba20f..00000000 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/lib/Migration.kt +++ /dev/null @@ -1,25 +0,0 @@ -package suwayomi.tachidesk.server.database.migration.lib - -/* - * Copyright (C) Contributors to the Suwayomi project - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -// originally licenced under MIT by Andreas Mausch, Changes are licenced under Mozilla Public License, v. 2.0. -// adopted from: https://gitlab.com/andreas-mausch/exposed-migrations/-/tree/4bf853c18a24d0170eda896ddbb899cb01233595 - -abstract class Migration { - val name: String - val version: Int - - init { - val groups = Regex("^M(\\d+)_(.*)$").matchEntire(this::class.simpleName!!)?.groupValues - ?: throw IllegalArgumentException("Migration class name doesn't match convention") - version = groups[1].toInt() - name = groups[2] - } - - abstract fun run() -} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/lib/MigrationEntity.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/lib/MigrationEntity.kt deleted file mode 100644 index 04c71c0c..00000000 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/lib/MigrationEntity.kt +++ /dev/null @@ -1,37 +0,0 @@ -package suwayomi.tachidesk.server.database.migration.lib - -/* - * Copyright (C) Contributors to the Suwayomi project - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -// originally licenced under MIT by Andreas Mausch, Changes are licenced under Mozilla Public License, v. 2.0. -// adopted from: https://gitlab.com/andreas-mausch/exposed-migrations/-/tree/4bf853c18a24d0170eda896ddbb899cb01233595 - -import org.jetbrains.exposed.dao.IntEntity -import org.jetbrains.exposed.dao.IntEntityClass -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.dao.id.IdTable -import org.jetbrains.exposed.sql.`java-time`.timestamp - -object MigrationsTable : IdTable() { - override val id = integer("version").entityId() - override val primaryKey = PrimaryKey(id) - - val name = varchar("name", length = 400) - val executedAt = timestamp("executed_at") - - init { - index(true, name) - } -} - -class MigrationEntity(id: EntityID) : IntEntity(id) { - companion object : IntEntityClass(MigrationsTable) - - var version by MigrationsTable.id - var name by MigrationsTable.name - var executedAt by MigrationsTable.executedAt -} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/lib/runMigrations.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/lib/runMigrations.kt deleted file mode 100644 index cf1dc338..00000000 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/lib/runMigrations.kt +++ /dev/null @@ -1,123 +0,0 @@ -package suwayomi.tachidesk.server.database.migration.lib - -/* - * Copyright (C) Contributors to the Suwayomi project - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -// originally licenced under MIT by Andreas Mausch, Changes are licenced under Mozilla Public License, v. 2.0. -// adopted from: https://gitlab.com/andreas-mausch/exposed-migrations/-/tree/4bf853c18a24d0170eda896ddbb899cb01233595 - -import mu.KotlinLogging -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.sql.Database -import org.jetbrains.exposed.sql.SchemaUtils.create -import org.jetbrains.exposed.sql.exists -import org.jetbrains.exposed.sql.transactions.TransactionManager -import org.jetbrains.exposed.sql.transactions.transaction -import suwayomi.tachidesk.server.ServerConfig -import java.nio.file.FileSystems -import java.nio.file.Files -import java.nio.file.Paths -import java.time.Clock -import java.time.Instant.now -import kotlin.io.path.ExperimentalPathApi -import kotlin.io.path.isDirectory -import kotlin.io.path.name -import kotlin.streams.toList - -private val logger = KotlinLogging.logger {} - -fun runMigrations(migrations: List, database: Database = TransactionManager.defaultDatabase!!, clock: Clock = Clock.systemUTC()) { - checkVersions(migrations) - - logger.info { "Running migrations on database ${database.url}" } - - val latestVersion = transaction(database) { - createTableIfNotExists(database) - MigrationEntity.all().maxByOrNull { it.version }?.version?.value - } - - logger.info { "Database version before migrations: $latestVersion" } - - migrations - .sortedBy { it.version } - .filter { shouldRun(latestVersion, it) } - .forEach { - logger.info { "Running migration version ${it.version}: ${it.name}" } - transaction(database) { - it.run() - - MigrationEntity.new { - version = EntityID(it.version, MigrationsTable) - name = it.name - executedAt = now(clock) - } - } - } - - logger.info { "Migrations finished successfully" } -} - -@OptIn(ExperimentalPathApi::class) -private fun getTopLevelClasses(packageName: String): List> { - ServerConfig::class.java.getResource("/" + packageName.replace('.', '/')) - val path = "/" + packageName.replace('.', '/') - val uri = ServerConfig::class.java.getResource(path).toURI() - - return when (uri.scheme) { - "jar" -> { - val fileSystem = FileSystems.newFileSystem(uri, emptyMap()) - fileSystem.getPath(path) - } - else -> Paths.get(uri) - }.let { Files.walk(it, 1) } - .toList() - .filterNot { it.isDirectory() || it.name.contains('$') } // '$' means it's not a top level class - .filter { it.name.endsWith(".class") } - .map { Class.forName("$packageName.${it.name.substringBefore(".class")}") } -} - -@Suppress("UnstableApiUsage") -fun loadMigrationsFrom(packageName: String): List { - return getTopLevelClasses(packageName) - .map { - logger.debug("found Migration class ${it.name}") - val clazz = it.getDeclaredConstructor().newInstance() - if (clazz is Migration) - clazz - else - throw RuntimeException("found a class that's not a Migration") - } -} - -private fun checkVersions(migrations: List) { - val sorted = migrations.map { it.version }.sorted() - if ((1..migrations.size).toList() != sorted) { - throw IllegalStateException("List of migrations version is not consecutive: $sorted") - } -} - -private fun createTableIfNotExists(database: Database) { - if (MigrationsTable.exists()) { - return - } - val tableNames = database.dialect.allTablesNames() - when (tableNames.isEmpty()) { - true -> { - logger.info { "Empty database found, creating table for migrations" } - create(MigrationsTable) - } - false -> throw IllegalStateException("Tried to run migrations against a non-empty database without a Migrations table. This is not supported.") - } -} - -private fun shouldRun(latestVersion: Int?, migration: Migration): Boolean { - val run = latestVersion?.let { migration.version > it } ?: true - if (!run) { - logger.debug { "Skipping migration version ${migration.version}: ${migration.name}" } - } - return run -}