mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-11-13 12:13:42 +01:00
Embed migrations into Rust binary
This commit is contained in:
1 parent
d0fde99b1c
commit
1abe01aa5a
51 files changed
+44
-31
No files matched your search
@@ -27,7 +27,7 @@ async function createMigration() {
|
||||
|
||||
const timestamp = generateTimestamp();
|
||||
const fileName = `${timestamp}_${slugify(String(migrationName), { lower: true })}.sql`;
|
||||
const migrationsDir = path.join(__dirname, '../src-tauri/migrations');
|
||||
const migrationsDir = path.join(__dirname, '../src-tauri/yaak-models/migrations');
|
||||
const filePath = path.join(migrationsDir, fileName);
|
||||
|
||||
if (!fs.existsSync(migrationsDir)) {
|
||||
|
||||
Generated
+20
@@ -2475,6 +2475,25 @@ dependencies = [
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "include_dir"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
|
||||
dependencies = [
|
||||
"include_dir_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "include_dir_macros"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@@ -7654,6 +7673,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"hex",
|
||||
"include_dir",
|
||||
"log",
|
||||
"nanoid",
|
||||
"r2d2",
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
],
|
||||
"longDescription": "A cross-platform desktop app for interacting with REST, GraphQL, and gRPC",
|
||||
"resources": [
|
||||
"migrations",
|
||||
"vendored/protoc/include",
|
||||
"vendored/plugins",
|
||||
"vendored/plugin-runtime"
|
||||
|
||||
@@ -7,6 +7,8 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.38", features = ["serde"] }
|
||||
hex = "0.4.3"
|
||||
include_dir = "0.7"
|
||||
log = "0.4.22"
|
||||
nanoid = "0.4.0"
|
||||
r2d2 = "0.8.10"
|
||||
@@ -22,7 +24,6 @@ tauri-plugin-dialog = { workspace = true }
|
||||
thiserror = "2.0.11"
|
||||
tokio = { workspace = true }
|
||||
ts-rs = { workspace = true, features = ["chrono-impl", "serde-json-impl"] }
|
||||
hex = "0.4.3"
|
||||
|
||||
[build-dependencies]
|
||||
tauri-plugin = { workspace = true, features = ["build"] }
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -59,7 +59,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
.build(manager)
|
||||
.unwrap();
|
||||
|
||||
if let Err(e) = migrate_db(app_handle.app_handle(), &pool) {
|
||||
if let Err(e) = migrate_db(&pool) {
|
||||
error!("Failed to run database migration {e:?}");
|
||||
app_handle
|
||||
.dialog()
|
||||
|
||||
@@ -1,26 +1,16 @@
|
||||
use crate::error::Error::MigrationError;
|
||||
use crate::error::Result;
|
||||
use log::info;
|
||||
use include_dir::{include_dir, Dir, DirEntry};
|
||||
use log::{debug, info};
|
||||
use r2d2::Pool;
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use rusqlite::{OptionalExtension, TransactionBehavior, params};
|
||||
use rusqlite::{params, OptionalExtension, TransactionBehavior};
|
||||
use sha2::{Digest, Sha384};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::result::Result as StdResult;
|
||||
use tauri::path::BaseDirectory;
|
||||
use tauri::{AppHandle, Manager, Runtime};
|
||||
|
||||
pub(crate) fn migrate_db<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
pool: &Pool<SqliteConnectionManager>,
|
||||
) -> Result<()> {
|
||||
let migrations_dir = app_handle
|
||||
.path()
|
||||
.resolve("migrations", BaseDirectory::Resource)
|
||||
.expect("failed to resolve resource");
|
||||
static MIGRATIONS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/migrations");
|
||||
|
||||
info!("Running database migrations from: {:?}", migrations_dir);
|
||||
pub(crate) fn migrate_db(pool: &Pool<SqliteConnectionManager>) -> Result<()> {
|
||||
info!("Running database migrations");
|
||||
|
||||
// Ensure the table exists
|
||||
// NOTE: Yaak used to use sqlx for migrations, so we need to mirror that table structure. We
|
||||
@@ -39,20 +29,22 @@ pub(crate) fn migrate_db<R: Runtime>(
|
||||
)?;
|
||||
|
||||
// Read and sort all .sql files
|
||||
let mut entries = fs::read_dir(migrations_dir)
|
||||
.expect("Failed to find migrations directory")
|
||||
.filter_map(StdResult::ok)
|
||||
let mut entries = MIGRATIONS_DIR
|
||||
.entries()
|
||||
.into_iter()
|
||||
.filter(|e| e.path().extension().map(|ext| ext == "sql").unwrap_or(false))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Ensure they're in the correct order
|
||||
entries.sort_by_key(|e| e.file_name());
|
||||
entries.sort_by_key(|e| e.path());
|
||||
|
||||
// Run each migration in a transaction
|
||||
let mut num_migrations = 0;
|
||||
for entry in entries {
|
||||
num_migrations += 1;
|
||||
let mut conn = pool.get()?;
|
||||
let mut tx = conn.transaction_with_behavior(TransactionBehavior::Immediate)?;
|
||||
match run_migration(entry.path().as_path(), &mut tx) {
|
||||
match run_migration(entry, &mut tx) {
|
||||
Ok(_) => tx.commit()?,
|
||||
Err(e) => {
|
||||
let msg = format!(
|
||||
@@ -66,16 +58,15 @@ pub(crate) fn migrate_db<R: Runtime>(
|
||||
};
|
||||
}
|
||||
|
||||
info!("Finished running migrations");
|
||||
info!("Finished running {} migrations", num_migrations);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_migration(migration_path: &Path, tx: &mut rusqlite::Transaction) -> Result<bool> {
|
||||
fn run_migration(migration_path: &DirEntry, tx: &mut rusqlite::Transaction) -> Result<bool> {
|
||||
let start = std::time::Instant::now();
|
||||
let (version, description) =
|
||||
split_migration_filename(migration_path.file_name().unwrap().to_str().unwrap())
|
||||
.expect("Failed to parse migration filename");
|
||||
let (version, description) = split_migration_filename(migration_path.path().to_str().unwrap())
|
||||
.expect("Failed to parse migration filename");
|
||||
|
||||
// Skip if already applied
|
||||
let row: Option<i64> = tx
|
||||
@@ -85,11 +76,13 @@ fn run_migration(migration_path: &Path, tx: &mut rusqlite::Transaction) -> Resul
|
||||
.optional()?;
|
||||
|
||||
if row.is_some() {
|
||||
debug!("Skipping migration {description}");
|
||||
// Migration was already run
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let sql = fs::read_to_string(migration_path).expect("Failed to read migration file");
|
||||
let sql =
|
||||
migration_path.as_file().unwrap().contents_utf8().expect("Failed to read migration file");
|
||||
info!("Applying migration {description}");
|
||||
|
||||
// Split on `;`? → optional depending on how your SQL is structured
|
||||
|
||||
Reference in new issue
Block a user