diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c994e8c8..9ee5197b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,3 +8,4 @@ All notable Modrinth Plus changes are documented here. - Added Gitea Actions verification for the Modrinth Plus fork. - Added Windows installer publishing to the Gitea generic package registry. - Added Codex repository context and release/security documentation. +- Fixed startup compatibility with existing official Modrinth App databases that recorded a different checksum for the historical initial SQLite migration. diff --git a/packages/app-lib/src/state/db.rs b/packages/app-lib/src/state/db.rs index b2a180560..e43825c3e 100644 --- a/packages/app-lib/src/state/db.rs +++ b/packages/app-lib/src/state/db.rs @@ -6,6 +6,9 @@ use sqlx::{Pool, Sqlite}; use std::str::FromStr; use std::time::Duration; +static MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!(); +const HISTORICAL_INIT_MIGRATION_VERSION: i64 = 20240711194701; + pub(crate) async fn connect( app_identifier: &str, ) -> crate::Result> { @@ -31,7 +34,8 @@ pub(crate) async fn connect( .connect_with(conn_options) .await?; - sqlx::migrate!().run(&pool).await?; + reconcile_historical_migration_checksums(&pool).await?; + MIGRATOR.run(&pool).await?; if let Err(err) = stale_data_cleanup(&pool).await { tracing::warn!( @@ -42,6 +46,55 @@ pub(crate) async fn connect( Ok(pool) } +async fn reconcile_historical_migration_checksums( + pool: &Pool, +) -> crate::Result<()> { + let Some(init_migration) = MIGRATOR.iter().find(|migration| { + migration.version == HISTORICAL_INIT_MIGRATION_VERSION + }) + else { + return Ok(()); + }; + + let has_migration_table: i64 = sqlx::query_scalar( + "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = '_sqlx_migrations'", + ) + .fetch_one(pool) + .await?; + + if has_migration_table == 0 { + return Ok(()); + } + + let stored_checksum: Option> = sqlx::query_scalar( + "SELECT checksum FROM _sqlx_migrations WHERE version = ? AND success = TRUE", + ) + .bind(HISTORICAL_INIT_MIGRATION_VERSION) + .fetch_optional(pool) + .await?; + + if let Some(stored_checksum) = stored_checksum { + let bundled_checksum = init_migration.checksum.as_ref(); + + if stored_checksum != bundled_checksum { + tracing::warn!( + "Reconciling checksum for historical Modrinth init migration {}", + HISTORICAL_INIT_MIGRATION_VERSION + ); + + sqlx::query( + "UPDATE _sqlx_migrations SET checksum = ? WHERE version = ? AND success = TRUE", + ) + .bind(bundled_checksum) + .bind(HISTORICAL_INIT_MIGRATION_VERSION) + .execute(pool) + .await?; + } + } + + Ok(()) +} + /// Cleans up data from the database that is no longer referenced, but must be /// kept around for a little while to allow users to recover from accidental /// deletions.