Skip to content

Commit

Permalink
Rewrite Migrations (#577)
Browse files Browse the repository at this point in the history
* Rewrite Migrations

* Fix Detekt errors

* Do migrations synchronous

* Filter and sort migrations

* Review changes

* Review changes 2

* Fix Detekt errors
  • Loading branch information
ghostbear authored Mar 25, 2024
1 parent 6965e59 commit 666d6aa
Show file tree
Hide file tree
Showing 10 changed files with 275 additions and 84 deletions.
69 changes: 0 additions & 69 deletions app/src/main/java/eu/kanade/tachiyomi/Migrations.kt

This file was deleted.

35 changes: 20 additions & 15 deletions app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ import cafe.adriel.voyager.navigator.NavigatorDisposeBehavior
import cafe.adriel.voyager.navigator.currentOrThrow
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.components.AppStateBanners
import eu.kanade.presentation.components.DownloadedOnlyBannerBackgroundColor
import eu.kanade.presentation.components.IncognitoModeBannerBackgroundColor
Expand All @@ -61,7 +59,6 @@ import eu.kanade.presentation.more.settings.screen.data.RestoreBackupScreen
import eu.kanade.presentation.util.AssistContentScreen
import eu.kanade.presentation.util.DefaultNavigatorScreenTransition
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.Migrations
import eu.kanade.tachiyomi.data.cache.ChapterCache
import eu.kanade.tachiyomi.data.download.DownloadCache
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
Expand Down Expand Up @@ -89,7 +86,11 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import logcat.LogPriority
import mihon.core.migration.Migrator
import mihon.core.migration.migrations.migrations
import tachiyomi.core.common.Constants
import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.library.service.LibraryPreferences
Expand All @@ -105,9 +106,7 @@ import androidx.compose.ui.graphics.Color.Companion as ComposeColor

class MainActivity : BaseActivity() {

private val sourcePreferences: SourcePreferences by injectLazy()
private val libraryPreferences: LibraryPreferences by injectLazy()
private val uiPreferences: UiPreferences by injectLazy()
private val preferences: BasePreferences by injectLazy()

private val downloadCache: DownloadCache by injectLazy()
Expand All @@ -130,16 +129,7 @@ class MainActivity : BaseActivity() {

super.onCreate(savedInstanceState)

val didMigration = if (isLaunch) {
Migrations.upgrade(
context = applicationContext,
preferenceStore = Injekt.get(),
sourcePreferences = Injekt.get(),
extensionRepoRepository = Injekt.get(),
)
} else {
false
}
val didMigration = migrate()

// Do not let the launcher create a new activity http://stackoverflow.com/questions/16283079
if (!isTaskRoot) {
Expand Down Expand Up @@ -350,6 +340,21 @@ class MainActivity : BaseActivity() {
}
}

private fun migrate(): Boolean {
val preferenceStore = Injekt.get<PreferenceStore>()
val preference = preferenceStore.getInt(Preference.appStateKey("last_version_code"), 0)
logcat { "Migration from ${preference.get()} to ${BuildConfig.VERSION_CODE}" }
return Migrator.migrate(
old = preference.get(),
new = BuildConfig.VERSION_CODE,
migrations = migrations,
onMigrationComplete = {
logcat { "Updating last version to ${BuildConfig.VERSION_CODE}" }
preference.set(BuildConfig.VERSION_CODE)
},
)
}

/**
* Sets custom splash screen exit animation on devices prior to Android 12.
*
Expand Down
19 changes: 19 additions & 0 deletions app/src/main/java/mihon/core/migration/Migration.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package mihon.core.migration

interface Migration {
val version: Float

suspend operator fun invoke(migrationContext: MigrationContext): Boolean

companion object {
const val ALWAYS = -1f

fun of(version: Float, action: suspend (MigrationContext) -> Boolean): Migration = object : Migration {
override val version: Float = version

override suspend operator fun invoke(migrationContext: MigrationContext): Boolean {
return action(migrationContext)
}
}
}
}
10 changes: 10 additions & 0 deletions app/src/main/java/mihon/core/migration/MigrationContext.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package mihon.core.migration

import uy.kohesive.injekt.Injekt

class MigrationContext {

inline fun <reified T> get(): T? {
return Injekt.getInstanceOrNull(T::class.java)
}
}
53 changes: 53 additions & 0 deletions app/src/main/java/mihon/core/migration/Migrator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package mihon.core.migration

import kotlinx.coroutines.runBlocking
import tachiyomi.core.common.util.system.logcat

object Migrator {

@SuppressWarnings("ReturnCount")
fun migrate(
old: Int,
new: Int,
migrations: List<Migration>,
dryrun: Boolean = false,
onMigrationComplete: () -> Unit
): Boolean {
val migrationContext = MigrationContext()

if (old == 0) {
return migrationContext.migrate(
migrations = migrations.filter { it.isAlways() },
dryrun = dryrun
)
.also { onMigrationComplete() }
}

if (old >= new) {
return false
}

return migrationContext.migrate(
migrations = migrations.filter { it.isAlways() || it.version.toInt() in (old + 1)..new },
dryrun = dryrun
)
.also { onMigrationComplete() }
}

private fun Migration.isAlways() = version == Migration.ALWAYS

@SuppressWarnings("MaxLineLength")
private fun MigrationContext.migrate(migrations: List<Migration>, dryrun: Boolean): Boolean {
return migrations.sortedBy { it.version }
.map { migration ->
if (!dryrun) {
logcat { "Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" }
runBlocking { migration(this@migrate) }
} else {
logcat { "(Dry-run) Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" }
true
}
}
.reduce { acc, b -> acc || b }
}
}
10 changes: 10 additions & 0 deletions app/src/main/java/mihon/core/migration/migrations/Migrations.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package mihon.core.migration.migrations

import mihon.core.migration.Migration

val migrations: List<Migration>
get() = listOf(
SetupBackupCreateMigration(),
SetupLibraryUpdateMigration(),
TrustExtensionRepositoryMigration(),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package mihon.core.migration.migrations

import eu.kanade.tachiyomi.App
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
import mihon.core.migration.Migration
import mihon.core.migration.MigrationContext

class SetupBackupCreateMigration : Migration {
override val version: Float = Migration.ALWAYS

override suspend fun invoke(migrationContext: MigrationContext): Boolean {
val context = migrationContext.get<App>() ?: return false
BackupCreateJob.setupTask(context)
return true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package mihon.core.migration.migrations

import eu.kanade.tachiyomi.App
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import mihon.core.migration.Migration
import mihon.core.migration.MigrationContext

class SetupLibraryUpdateMigration : Migration {
override val version: Float = Migration.ALWAYS

override suspend fun invoke(migrationContext: MigrationContext): Boolean {
val context = migrationContext.get<App>() ?: return false
LibraryUpdateJob.setupTask(context)
return true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package mihon.core.migration.migrations

import eu.kanade.domain.source.service.SourcePreferences
import logcat.LogPriority
import mihon.core.migration.Migration
import mihon.core.migration.MigrationContext
import mihon.domain.extensionrepo.exception.SaveExtensionRepoException
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.core.common.util.system.logcat

class TrustExtensionRepositoryMigration : Migration {
override val version: Float = 7f

override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
val sourcePreferences = migrationContext.get<SourcePreferences>() ?: return@withIOContext false
val extensionRepositoryRepository =
migrationContext.get<ExtensionRepoRepository>() ?: return@withIOContext false
for ((index, source) in sourcePreferences.extensionRepos().get().withIndex()) {
try {
extensionRepositoryRepository.upsertRepo(
source,
"Repo #${index + 1}",
null,
source,
"NOFINGERPRINT-${index + 1}",
)
} catch (e: SaveExtensionRepoException) {
logcat(LogPriority.ERROR, e) { "Error Migrating Extension Repo with baseUrl: $source" }
}
}
sourcePreferences.extensionRepos().delete()
return@withIOContext true
}
}
Loading

0 comments on commit 666d6aa

Please sign in to comment.