From edf1b07d729a03e2fe9bce49731ac2d61b79991f Mon Sep 17 00:00:00 2001 From: Giorgi Merebashvili Date: Wed, 10 Dec 2025 13:38:49 +0400 Subject: [PATCH 01/13] Add sea-orm.toml config file feature --- sea-orm-cli/Cargo.toml | 2 + sea-orm-cli/src/cli.rs | 46 ++++++----------- sea-orm-cli/src/commands/config.rs | 20 ++++++++ sea-orm-cli/src/commands/migrate.rs | 29 ++++++----- sea-orm-cli/src/commands/mod.rs | 2 + sea-orm-cli/src/config.rs | 77 +++++++++++++++++++++++++++++ sea-orm-cli/src/lib.rs | 2 + sea-orm-cli/template/sea-orm.toml | 5 ++ 8 files changed, 137 insertions(+), 46 deletions(-) create mode 100644 sea-orm-cli/src/commands/config.rs create mode 100644 sea-orm-cli/src/config.rs create mode 100644 sea-orm-cli/template/sea-orm.toml diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 4e62b9f3f8..1bf7ef34a2 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -63,6 +63,8 @@ tracing-subscriber = { version = "0.3.17", default-features = false, features = "fmt", ] } url = { version = "2.2", default-features = false } +toml = "0.9.8" +serde = { version = "1.0.228", features = ["derive"] } [dev-dependencies] smol = "1.2.5" diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index 66bb7e373f..0517296c96 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -3,7 +3,7 @@ use clap::{ArgAction, ArgGroup, Parser, Subcommand, ValueEnum}; use dotenvy::dotenv; #[cfg(feature = "codegen")] -use crate::{handle_error, run_generate_command, run_migrate_command}; +use crate::{handle_error, run_config_command, run_generate_command, run_migrate_command}; #[derive(Parser, Debug)] #[command( @@ -53,31 +53,22 @@ pub struct Cli { #[allow(clippy::large_enum_variant)] #[derive(Subcommand, PartialEq, Eq, Debug)] pub enum Commands { + #[command(about = "Config related commands", display_order = 10)] + Config { + #[command(subcommand)] + command: ConfigSubcommands, + }, #[command( about = "Codegen related commands", arg_required_else_help = true, - display_order = 10 + display_order = 20 )] Generate { #[command(subcommand)] command: GenerateSubcommands, }, - #[command(about = "Migration related commands", display_order = 20)] + #[command(about = "Migration related commands", display_order = 30)] Migrate { - #[arg( - global = true, - short = 'd', - long, - env = "MIGRATION_DIR", - help = "Migration script directory. -If your migrations are in their own crate, -you can provide the root of that crate. -If your migrations are in a submodule of your app, -you should provide the directory of that submodule.", - default_value = "./migration" - )] - migration_dir: String, - #[arg( global = true, short = 's', @@ -89,21 +80,17 @@ you should provide the directory of that submodule.", )] database_schema: Option, - #[arg( - global = true, - short = 'u', - long, - env = "DATABASE_URL", - help = "Database URL", - hide_env_values = true - )] - database_url: Option, - #[command(subcommand)] command: Option, }, } +#[derive(Subcommand, PartialEq, Eq, Debug)] +pub enum ConfigSubcommands { + #[command(about = "Initialize config file", display_order = 10)] + Init, +} + #[derive(Subcommand, PartialEq, Eq, Debug)] pub enum MigrateSubcommands { #[command(about = "Initialize migration directory", display_order = 10)] @@ -417,21 +404,18 @@ pub async fn main() { let verbose = cli.verbose; match cli.command { + Commands::Config { command } => run_config_command(command).unwrap_or_else(handle_error), Commands::Generate { command } => { run_generate_command(command, verbose) .await .unwrap_or_else(handle_error); } Commands::Migrate { - migration_dir, database_schema, - database_url, command, } => run_migrate_command( command, - &migration_dir, database_schema, - database_url, verbose, ) .unwrap_or_else(handle_error), diff --git a/sea-orm-cli/src/commands/config.rs b/sea-orm-cli/src/commands/config.rs new file mode 100644 index 0000000000..475fd5877f --- /dev/null +++ b/sea-orm-cli/src/commands/config.rs @@ -0,0 +1,20 @@ +use std::path::Path; +use std::{error::Error, fs}; + +use crate::ConfigSubcommands; + +pub fn run_config_command(command: ConfigSubcommands) -> Result<(), Box> { + match command { + ConfigSubcommands::Init => run_config_init(), + } +} + +fn run_config_init() -> Result<(), Box> { + let config_path = Path::new("sea-orm.toml"); + let config_template = include_str!("../../template/sea-orm.toml"); + + fs::write(config_path, config_template.to_string())?; + + println!("Config file created at {}", config_path.display()); + Ok(()) +} diff --git a/sea-orm-cli/src/commands/migrate.rs b/sea-orm-cli/src/commands/migrate.rs index 11978b4dd8..7052a97ed4 100644 --- a/sea-orm-cli/src/commands/migrate.rs +++ b/sea-orm-cli/src/commands/migrate.rs @@ -10,34 +10,34 @@ use std::{ }; #[cfg(feature = "cli")] -use crate::MigrateSubcommands; +use crate::{config::get_migration_dir, config::get_database_url, MigrateSubcommands}; #[cfg(feature = "cli")] pub fn run_migrate_command( command: Option, - migration_dir: &str, database_schema: Option, - database_url: Option, verbose: bool, ) -> Result<(), Box> { + let migration_dir = get_migration_dir()?; + let database_url = get_database_url()?; match command { - Some(MigrateSubcommands::Init) => run_migrate_init(migration_dir)?, + Some(MigrateSubcommands::Init) => run_migrate_init(&migration_dir)?, Some(MigrateSubcommands::Generate { migration_name, universal_time: _, local_time, - }) => run_migrate_generate(migration_dir, &migration_name, !local_time)?, + }) => run_migrate_generate(&migration_dir, &migration_name, !local_time)?, _ => { let (subcommand, migration_dir, steps, verbose) = match command { - Some(MigrateSubcommands::Fresh) => ("fresh", migration_dir, None, verbose), - Some(MigrateSubcommands::Refresh) => ("refresh", migration_dir, None, verbose), - Some(MigrateSubcommands::Reset) => ("reset", migration_dir, None, verbose), - Some(MigrateSubcommands::Status) => ("status", migration_dir, None, verbose), - Some(MigrateSubcommands::Up { num }) => ("up", migration_dir, num, verbose), + Some(MigrateSubcommands::Fresh) => ("fresh", &migration_dir, None, verbose), + Some(MigrateSubcommands::Refresh) => ("refresh", &migration_dir, None, verbose), + Some(MigrateSubcommands::Reset) => ("reset", &migration_dir, None, verbose), + Some(MigrateSubcommands::Status) => ("status", &migration_dir, None, verbose), + Some(MigrateSubcommands::Up { num }) => ("up", &migration_dir, num, verbose), Some(MigrateSubcommands::Down { num }) => { - ("down", migration_dir, Some(num), verbose) + ("down", &migration_dir, Some(num), verbose) } - _ => ("up", migration_dir, None, verbose), + _ => ("up", &migration_dir, None, verbose), }; // Construct the `--manifest-path` @@ -57,9 +57,8 @@ pub fn run_migrate_command( if !num.is_empty() { args.extend(["-n", &num]) } - if let Some(database_url) = &database_url { - envs.push(("DATABASE_URL", database_url)); - } + envs.push(("DATABASE_URL", &database_url)); + if let Some(database_schema) = &database_schema { envs.push(("DATABASE_SCHEMA", database_schema)); } diff --git a/sea-orm-cli/src/commands/mod.rs b/sea-orm-cli/src/commands/mod.rs index 4ef1dd2e64..5bfcb0a166 100644 --- a/sea-orm-cli/src/commands/mod.rs +++ b/sea-orm-cli/src/commands/mod.rs @@ -1,10 +1,12 @@ use std::fmt::Display; #[cfg(feature = "codegen")] +pub mod config; pub mod generate; pub mod migrate; #[cfg(feature = "codegen")] +pub use config::*; pub use generate::*; pub use migrate::*; diff --git a/sea-orm-cli/src/config.rs b/sea-orm-cli/src/config.rs new file mode 100644 index 0000000000..5dd69ca6bd --- /dev/null +++ b/sea-orm-cli/src/config.rs @@ -0,0 +1,77 @@ +use serde::Deserialize; +use std::path::{Path, PathBuf}; +use std::{env, error::Error, fs}; + +#[derive(Deserialize)] +pub struct Config { + database: Database, + migration: Migration, +} + +#[derive(Deserialize)] +struct Database { + url: String, +} + +#[derive(Deserialize)] +struct Migration { + directory: String, +} + +/// The config file is expected to be in the current directory or a parent directory +pub fn get_config() -> Result> { + let config_path = find_config_file()?; + + let file_content = fs::read_to_string(config_path)?; + let config: Config = toml::from_str(&file_content)?; + Ok(config) +} + +pub fn get_database_url() -> Result> { + let config = get_config()?; + if config.database.url.starts_with("env:") { + let env_var = config.database.url.split(":").nth(1).unwrap(); + let value = env::var(env_var)?; + Ok(value) + } else { + Ok(config.database.url) + } +} + +pub fn get_migration_dir() -> Result> { + let config = get_config()?; + + let migration_dir = config.migration.directory; + if migration_dir.starts_with(".") { + let config_dir = get_config_dir()?; + let migration_dir = config_dir.join(migration_dir); + return Ok(migration_dir.to_string_lossy().to_string()); + } else { + return Ok(migration_dir); + } +} + +pub fn get_config_dir() -> Result> { + let config_path = find_config_file()?; + let config_dir = config_path.parent().unwrap_or(Path::new(".")).to_path_buf(); + Ok(config_dir) +} + +fn find_config_file() -> Result> { + let current_dir = std::env::current_dir()?; + let config_path = current_dir.join("sea-orm.toml"); + if config_path.exists() { + return Ok(config_path); + } + let parent_dir = current_dir.parent(); + if let Some(parent_dir) = parent_dir { + let config_path = parent_dir.join("sea-orm.toml"); + if config_path.exists() { + return Ok(config_path); + } + } + Err(Box::new(std::io::Error::new( + std::io::ErrorKind::NotFound, + "SeaORM config file not found, use `sea-orm-cli config inint` to create one", + ))) +} diff --git a/sea-orm-cli/src/lib.rs b/sea-orm-cli/src/lib.rs index 4cbb297018..156d7d9fe7 100644 --- a/sea-orm-cli/src/lib.rs +++ b/sea-orm-cli/src/lib.rs @@ -1,7 +1,9 @@ #[cfg(feature = "cli")] pub mod cli; pub mod commands; +pub mod config; #[cfg(feature = "cli")] pub use cli::*; pub use commands::*; +pub use config::*; diff --git a/sea-orm-cli/template/sea-orm.toml b/sea-orm-cli/template/sea-orm.toml new file mode 100644 index 0000000000..4b73a95883 --- /dev/null +++ b/sea-orm-cli/template/sea-orm.toml @@ -0,0 +1,5 @@ +[database] +url = "env:DATABASE_URL" # Support env: prefix or direct URL + +[migration] +directory = "./migration" # Where migrations live, relative path \ No newline at end of file From 8e5e06bd76d09b707215b002cf0023f3104bfa25 Mon Sep 17 00:00:00 2001 From: Giorgi Merebashvili Date: Wed, 10 Dec 2025 13:40:59 +0400 Subject: [PATCH 02/13] Fix typo --- sea-orm-cli/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sea-orm-cli/src/config.rs b/sea-orm-cli/src/config.rs index 5dd69ca6bd..115a39122a 100644 --- a/sea-orm-cli/src/config.rs +++ b/sea-orm-cli/src/config.rs @@ -72,6 +72,6 @@ fn find_config_file() -> Result> { } Err(Box::new(std::io::Error::new( std::io::ErrorKind::NotFound, - "SeaORM config file not found, use `sea-orm-cli config inint` to create one", + "SeaORM config file not found, use `sea-orm-cli config init` to create one", ))) } From 03587f344e5ab6995c38a3464a1789396b86d1cb Mon Sep 17 00:00:00 2001 From: Giorgi Merebashvili Date: Wed, 10 Dec 2025 14:12:00 +0400 Subject: [PATCH 03/13] Change get_config_dir to private function --- sea-orm-cli/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sea-orm-cli/src/config.rs b/sea-orm-cli/src/config.rs index 115a39122a..8150c0bb61 100644 --- a/sea-orm-cli/src/config.rs +++ b/sea-orm-cli/src/config.rs @@ -51,7 +51,7 @@ pub fn get_migration_dir() -> Result> { } } -pub fn get_config_dir() -> Result> { +fn get_config_dir() -> Result> { let config_path = find_config_file()?; let config_dir = config_path.parent().unwrap_or(Path::new(".")).to_path_buf(); Ok(config_dir) From 1c5a6ef886f29dfaf42fc486d968d561e78a8d98 Mon Sep 17 00:00:00 2001 From: Giorgi Merebashvili Date: Wed, 10 Dec 2025 23:38:44 +0400 Subject: [PATCH 04/13] Improve sea-orm.toml migrations config --- sea-orm-cli/src/config.rs | 8 ++++---- sea-orm-cli/template/sea-orm.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sea-orm-cli/src/config.rs b/sea-orm-cli/src/config.rs index 8150c0bb61..5d38a8f724 100644 --- a/sea-orm-cli/src/config.rs +++ b/sea-orm-cli/src/config.rs @@ -5,7 +5,7 @@ use std::{env, error::Error, fs}; #[derive(Deserialize)] pub struct Config { database: Database, - migration: Migration, + migrations: Migrations, } #[derive(Deserialize)] @@ -14,7 +14,7 @@ struct Database { } #[derive(Deserialize)] -struct Migration { +struct Migrations { directory: String, } @@ -40,8 +40,8 @@ pub fn get_database_url() -> Result> { pub fn get_migration_dir() -> Result> { let config = get_config()?; - - let migration_dir = config.migration.directory; + + let migration_dir = config.migrations.directory; if migration_dir.starts_with(".") { let config_dir = get_config_dir()?; let migration_dir = config_dir.join(migration_dir); diff --git a/sea-orm-cli/template/sea-orm.toml b/sea-orm-cli/template/sea-orm.toml index 4b73a95883..7019fcb6ba 100644 --- a/sea-orm-cli/template/sea-orm.toml +++ b/sea-orm-cli/template/sea-orm.toml @@ -1,5 +1,5 @@ [database] url = "env:DATABASE_URL" # Support env: prefix or direct URL -[migration] +[migrations] directory = "./migration" # Where migrations live, relative path \ No newline at end of file From d3760e8e8413702aa5634793f430b7a344db4d17 Mon Sep 17 00:00:00 2001 From: Giorgi Merebashvili Date: Wed, 10 Dec 2025 23:38:53 +0400 Subject: [PATCH 05/13] Formats --- sea-orm-cli/src/cli.rs | 7 +------ sea-orm-cli/src/commands/migrate.rs | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index 0517296c96..dea43626d9 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -413,11 +413,6 @@ pub async fn main() { Commands::Migrate { database_schema, command, - } => run_migrate_command( - command, - database_schema, - verbose, - ) - .unwrap_or_else(handle_error), + } => run_migrate_command(command, database_schema, verbose).unwrap_or_else(handle_error), } } diff --git a/sea-orm-cli/src/commands/migrate.rs b/sea-orm-cli/src/commands/migrate.rs index 7052a97ed4..b88d81e910 100644 --- a/sea-orm-cli/src/commands/migrate.rs +++ b/sea-orm-cli/src/commands/migrate.rs @@ -10,7 +10,7 @@ use std::{ }; #[cfg(feature = "cli")] -use crate::{config::get_migration_dir, config::get_database_url, MigrateSubcommands}; +use crate::{MigrateSubcommands, config::get_database_url, config::get_migration_dir}; #[cfg(feature = "cli")] pub fn run_migrate_command( From f78cdddf0059abc98497f26dfefc86f46c72882c Mon Sep 17 00:00:00 2001 From: Giorgi Merebashvili Date: Thu, 11 Dec 2025 00:24:13 +0400 Subject: [PATCH 06/13] Update sea-orm-cli generate command to use database_url from config --- sea-orm-cli/src/cli.rs | 9 --------- sea-orm-cli/src/commands/generate.rs | 4 +++- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index dea43626d9..bd79510fbd 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -221,15 +221,6 @@ pub enum GenerateSubcommands { )] database_schema: Option, - #[arg( - short = 'u', - long, - env = "DATABASE_URL", - help = "Database URL", - hide_env_values = true - )] - database_url: String, - #[arg( long, default_value = "all", diff --git a/sea-orm-cli/src/commands/generate.rs b/sea-orm-cli/src/commands/generate.rs index 8cb4c7ba19..2140c4bf57 100644 --- a/sea-orm-cli/src/commands/generate.rs +++ b/sea-orm-cli/src/commands/generate.rs @@ -9,6 +9,8 @@ use std::{error::Error, fs, path::Path, process::Command, str::FromStr}; use tracing_subscriber::{EnvFilter, prelude::*}; use url::Url; +use crate::config::get_database_url; + pub async fn run_generate_command( command: GenerateSubcommands, verbose: bool, @@ -26,7 +28,6 @@ pub async fn run_generate_command( acquire_timeout, output_dir, database_schema, - database_url, with_prelude, with_serde, serde_skip_deserializing_primary_key, @@ -63,6 +64,7 @@ pub async fn run_generate_command( .try_init(); } + let database_url = get_database_url()?; // The database should be a valid URL that can be parsed // protocol://username:password@host/database_name let url = Url::parse(&database_url)?; From fb2a43f5d47b6593a7f1ef996ba6d0e6bd9d025f Mon Sep 17 00:00:00 2001 From: Giorgi Merebashvili Date: Thu, 11 Dec 2025 00:33:44 +0400 Subject: [PATCH 07/13] Refactor merge config.rs into commands/config.rs --- sea-orm-cli/src/commands/config.rs | 79 +++++++++++++++++++++++++++++- sea-orm-cli/src/config.rs | 77 ----------------------------- sea-orm-cli/src/lib.rs | 2 - 3 files changed, 77 insertions(+), 81 deletions(-) delete mode 100644 sea-orm-cli/src/config.rs diff --git a/sea-orm-cli/src/commands/config.rs b/sea-orm-cli/src/commands/config.rs index 475fd5877f..f22eaa8371 100644 --- a/sea-orm-cli/src/commands/config.rs +++ b/sea-orm-cli/src/commands/config.rs @@ -1,14 +1,89 @@ -use std::path::Path; -use std::{error::Error, fs}; +use serde::Deserialize; +use std::path::{Path, PathBuf}; +use std::{env, error::Error, fs}; use crate::ConfigSubcommands; +#[derive(Deserialize)] +pub struct Config { + database: Database, + migrations: Migrations, +} + +#[derive(Deserialize)] +struct Database { + url: String, +} + +#[derive(Deserialize)] +struct Migrations { + directory: String, +} + pub fn run_config_command(command: ConfigSubcommands) -> Result<(), Box> { match command { ConfigSubcommands::Init => run_config_init(), } } +/// The config file is expected to be in the current directory or a parent directory +pub fn get_config() -> Result> { + let config_path = find_config_file()?; + + let file_content = fs::read_to_string(config_path)?; + let config: Config = toml::from_str(&file_content)?; + Ok(config) +} + +pub fn get_database_url() -> Result> { + let config = get_config()?; + if config.database.url.starts_with("env:") { + let env_var = config.database.url.split(":").nth(1).unwrap(); + let value = env::var(env_var)?; + Ok(value) + } else { + Ok(config.database.url) + } +} + +pub fn get_migration_dir() -> Result> { + let config = get_config()?; + + let migration_dir = config.migrations.directory; + if migration_dir.starts_with(".") { + let config_dir = get_config_dir()?; + let migration_dir = config_dir.join(migration_dir); + return Ok(migration_dir.to_string_lossy().to_string()); + } else { + return Ok(migration_dir); + } +} + +fn get_config_dir() -> Result> { + let config_path = find_config_file()?; + let config_dir = config_path.parent().unwrap_or(Path::new(".")).to_path_buf(); + Ok(config_dir) +} + +fn find_config_file() -> Result> { + let current_dir = std::env::current_dir()?; + let config_path = current_dir.join("sea-orm.toml"); + if config_path.exists() { + return Ok(config_path); + } + let parent_dir = current_dir.parent(); + if let Some(parent_dir) = parent_dir { + let config_path = parent_dir.join("sea-orm.toml"); + if config_path.exists() { + return Ok(config_path); + } + } + Err(Box::new(std::io::Error::new( + std::io::ErrorKind::NotFound, + "SeaORM config file not found, use `sea-orm-cli config init` to create one", + ))) +} + fn run_config_init() -> Result<(), Box> { let config_path = Path::new("sea-orm.toml"); let config_template = include_str!("../../template/sea-orm.toml"); diff --git a/sea-orm-cli/src/config.rs b/sea-orm-cli/src/config.rs deleted file mode 100644 index 5d38a8f724..0000000000 --- a/sea-orm-cli/src/config.rs +++ /dev/null @@ -1,77 +0,0 @@ -use serde::Deserialize; -use std::path::{Path, PathBuf}; -use std::{env, error::Error, fs}; - -#[derive(Deserialize)] -pub struct Config { - database: Database, - migrations: Migrations, -} - -#[derive(Deserialize)] -struct Database { - url: String, -} - -#[derive(Deserialize)] -struct Migrations { - directory: String, -} - -/// The config file is expected to be in the current directory or a parent directory -pub fn get_config() -> Result> { - let config_path = find_config_file()?; - - let file_content = fs::read_to_string(config_path)?; - let config: Config = toml::from_str(&file_content)?; - Ok(config) -} - -pub fn get_database_url() -> Result> { - let config = get_config()?; - if config.database.url.starts_with("env:") { - let env_var = config.database.url.split(":").nth(1).unwrap(); - let value = env::var(env_var)?; - Ok(value) - } else { - Ok(config.database.url) - } -} - -pub fn get_migration_dir() -> Result> { - let config = get_config()?; - - let migration_dir = config.migrations.directory; - if migration_dir.starts_with(".") { - let config_dir = get_config_dir()?; - let migration_dir = config_dir.join(migration_dir); - return Ok(migration_dir.to_string_lossy().to_string()); - } else { - return Ok(migration_dir); - } -} - -fn get_config_dir() -> Result> { - let config_path = find_config_file()?; - let config_dir = config_path.parent().unwrap_or(Path::new(".")).to_path_buf(); - Ok(config_dir) -} - -fn find_config_file() -> Result> { - let current_dir = std::env::current_dir()?; - let config_path = current_dir.join("sea-orm.toml"); - if config_path.exists() { - return Ok(config_path); - } - let parent_dir = current_dir.parent(); - if let Some(parent_dir) = parent_dir { - let config_path = parent_dir.join("sea-orm.toml"); - if config_path.exists() { - return Ok(config_path); - } - } - Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "SeaORM config file not found, use `sea-orm-cli config init` to create one", - ))) -} diff --git a/sea-orm-cli/src/lib.rs b/sea-orm-cli/src/lib.rs index 156d7d9fe7..4cbb297018 100644 --- a/sea-orm-cli/src/lib.rs +++ b/sea-orm-cli/src/lib.rs @@ -1,9 +1,7 @@ #[cfg(feature = "cli")] pub mod cli; pub mod commands; -pub mod config; #[cfg(feature = "cli")] pub use cli::*; pub use commands::*; -pub use config::*; From 689890393c74b34ae958f6a563995d4890196f75 Mon Sep 17 00:00:00 2001 From: Giorgi Merebashvili Date: Thu, 11 Dec 2025 12:57:27 +0400 Subject: [PATCH 08/13] Revert CLI flags --- sea-orm-cli/src/cli.rs | 35 ++++++++++++++++++++++++++++ sea-orm-cli/src/commands/generate.rs | 1 + 2 files changed, 36 insertions(+) diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index bd79510fbd..8adf23ca80 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -69,6 +69,20 @@ pub enum Commands { }, #[command(about = "Migration related commands", display_order = 30)] Migrate { + #[arg( + global = true, + short = 'd', + long, + env = "MIGRATION_DIR", + help = "Migration script directory. +If your migrations are in their own crate, +you can provide the root of that crate. +If your migrations are in a submodule of your app, +you should provide the directory of that submodule.", + default_value = "./migration" + )] + migration_dir: String, + #[arg( global = true, short = 's', @@ -80,6 +94,16 @@ pub enum Commands { )] database_schema: Option, + #[arg( + global = true, + short = 'u', + long, + env = "DATABASE_URL", + help = "Database URL", + hide_env_values = true + )] + database_url: Option, + #[command(subcommand)] command: Option, }, @@ -221,6 +245,15 @@ pub enum GenerateSubcommands { )] database_schema: Option, + #[arg( + short = 'u', + long, + env = "DATABASE_URL", + help = "Database URL", + hide_env_values = true + )] + database_url: String, + #[arg( long, default_value = "all", @@ -402,7 +435,9 @@ pub async fn main() { .unwrap_or_else(handle_error); } Commands::Migrate { + migration_dir: _, database_schema, + database_url: _, command, } => run_migrate_command(command, database_schema, verbose).unwrap_or_else(handle_error), } diff --git a/sea-orm-cli/src/commands/generate.rs b/sea-orm-cli/src/commands/generate.rs index 2140c4bf57..9040f33419 100644 --- a/sea-orm-cli/src/commands/generate.rs +++ b/sea-orm-cli/src/commands/generate.rs @@ -28,6 +28,7 @@ pub async fn run_generate_command( acquire_timeout, output_dir, database_schema, + database_url: _, with_prelude, with_serde, serde_skip_deserializing_primary_key, From f67b978d29f2e79592fe769ef10c27728e7dc8d4 Mon Sep 17 00:00:00 2001 From: Giorgi Merebashvili Date: Thu, 11 Dec 2025 13:16:22 +0400 Subject: [PATCH 09/13] Fix use flag args when passed --- sea-orm-cli/src/cli.rs | 17 ++++++++++++----- sea-orm-cli/src/commands/generate.rs | 7 +++++-- sea-orm-cli/src/commands/migrate.rs | 12 ++++++++++-- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index 8adf23ca80..bc9c50cb45 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -81,7 +81,7 @@ If your migrations are in a submodule of your app, you should provide the directory of that submodule.", default_value = "./migration" )] - migration_dir: String, + migration_dir: Option, #[arg( global = true, @@ -252,7 +252,7 @@ pub enum GenerateSubcommands { help = "Database URL", hide_env_values = true )] - database_url: String, + database_url: Option, #[arg( long, @@ -435,10 +435,17 @@ pub async fn main() { .unwrap_or_else(handle_error); } Commands::Migrate { - migration_dir: _, + migration_dir, database_schema, - database_url: _, + database_url, + command, + } => run_migrate_command( command, - } => run_migrate_command(command, database_schema, verbose).unwrap_or_else(handle_error), + migration_dir, + database_schema, + database_url, + verbose, + ) + .unwrap_or_else(handle_error), } } diff --git a/sea-orm-cli/src/commands/generate.rs b/sea-orm-cli/src/commands/generate.rs index 9040f33419..5b99bb3d7c 100644 --- a/sea-orm-cli/src/commands/generate.rs +++ b/sea-orm-cli/src/commands/generate.rs @@ -28,7 +28,7 @@ pub async fn run_generate_command( acquire_timeout, output_dir, database_schema, - database_url: _, + database_url, with_prelude, with_serde, serde_skip_deserializing_primary_key, @@ -65,7 +65,10 @@ pub async fn run_generate_command( .try_init(); } - let database_url = get_database_url()?; + let database_url = match database_url { + Some(url) => url, + None => get_database_url()?, + }; // The database should be a valid URL that can be parsed // protocol://username:password@host/database_name let url = Url::parse(&database_url)?; diff --git a/sea-orm-cli/src/commands/migrate.rs b/sea-orm-cli/src/commands/migrate.rs index b88d81e910..9d9a2e51fb 100644 --- a/sea-orm-cli/src/commands/migrate.rs +++ b/sea-orm-cli/src/commands/migrate.rs @@ -15,11 +15,19 @@ use crate::{MigrateSubcommands, config::get_database_url, config::get_migration_ #[cfg(feature = "cli")] pub fn run_migrate_command( command: Option, + migration_dir: Option, database_schema: Option, + database_url: Option, verbose: bool, ) -> Result<(), Box> { - let migration_dir = get_migration_dir()?; - let database_url = get_database_url()?; + let migration_dir = match migration_dir { + Some(dir) => dir, + None => get_migration_dir()?, + }; + let database_url = match database_url { + Some(url) => url, + None => get_database_url()?, + }; match command { Some(MigrateSubcommands::Init) => run_migrate_init(&migration_dir)?, Some(MigrateSubcommands::Generate { From a1336ea5940d7dd95e1c06aa720b28639ff59465 Mon Sep 17 00:00:00 2001 From: Giorgi Merebashvili Date: Thu, 11 Dec 2025 15:48:36 +0400 Subject: [PATCH 10/13] Address review comments --- sea-orm-cli/Cargo.toml | 6 ++-- sea-orm-cli/src/cli.rs | 5 +++- sea-orm-cli/src/commands/config.rs | 45 ++++++++++++++++++++---------- sea-orm-cli/template/sea-orm.toml | 2 +- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 1bf7ef34a2..7f020518a8 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -63,14 +63,14 @@ tracing-subscriber = { version = "0.3.17", default-features = false, features = "fmt", ] } url = { version = "2.2", default-features = false } -toml = "0.9.8" -serde = { version = "1.0.228", features = ["derive"] } +toml = { version = "0.9.8", default-features = false, features = ["parse", "serde"], optional = true } +serde = { version = "1.0.228", features = ["derive"], optional = true } [dev-dependencies] smol = "1.2.5" [features] -cli = ["clap", "dotenvy"] +cli = ["clap", "dotenvy", "toml", "serde"] codegen = ["cli", "sqlx", "sea-schema", "sea-orm-codegen"] default = [ "codegen", diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index bc9c50cb45..819ca062b2 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -112,7 +112,10 @@ you should provide the directory of that submodule.", #[derive(Subcommand, PartialEq, Eq, Debug)] pub enum ConfigSubcommands { #[command(about = "Initialize config file", display_order = 10)] - Init, + Init { + #[arg(long, help = "Overwrite existing config file")] + force: bool + } } #[derive(Subcommand, PartialEq, Eq, Debug)] diff --git a/sea-orm-cli/src/commands/config.rs b/sea-orm-cli/src/commands/config.rs index f22eaa8371..522f76d451 100644 --- a/sea-orm-cli/src/commands/config.rs +++ b/sea-orm-cli/src/commands/config.rs @@ -10,9 +10,16 @@ pub struct Config { migrations: Migrations, } +#[derive(Deserialize)] +#[serde(untagged)] +enum DatabaseUrl { + Direct(String), + FromEnv { env: String }, +} + #[derive(Deserialize)] struct Database { - url: String, + url: DatabaseUrl, } #[derive(Deserialize)] @@ -22,7 +29,9 @@ struct Migrations { pub fn run_config_command(command: ConfigSubcommands) -> Result<(), Box> { match command { - ConfigSubcommands::Init => run_config_init(), + ConfigSubcommands::Init{ + force + } => run_config_init(force), } } @@ -37,25 +46,24 @@ pub fn get_config() -> Result> { pub fn get_database_url() -> Result> { let config = get_config()?; - if config.database.url.starts_with("env:") { - let env_var = config.database.url.split(":").nth(1).unwrap(); - let value = env::var(env_var)?; - Ok(value) - } else { - Ok(config.database.url) - } + let url = match config.database.url { + DatabaseUrl::Direct(url) => url, + DatabaseUrl::FromEnv { env } => env::var(&env)?, + }; + + Ok(url) } pub fn get_migration_dir() -> Result> { let config = get_config()?; - let migration_dir = config.migrations.directory; - if migration_dir.starts_with(".") { + let migration_dir = Path::new(&config.migrations.directory); + if migration_dir.is_relative() { let config_dir = get_config_dir()?; let migration_dir = config_dir.join(migration_dir); - return Ok(migration_dir.to_string_lossy().to_string()); + Ok(migration_dir.to_string_lossy().to_string()) } else { - return Ok(migration_dir); + Ok(config.migrations.directory) } } @@ -84,11 +92,18 @@ fn find_config_file() -> Result> { ))) } -fn run_config_init() -> Result<(), Box> { +fn run_config_init(force: bool) -> Result<(), Box> { let config_path = Path::new("sea-orm.toml"); let config_template = include_str!("../../template/sea-orm.toml"); - fs::write(config_path, config_template.to_string())?; + if config_path.exists() && !force { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::AlreadyExists, + "A sea-orm.toml file already exists, use --force to rewrite it" + ))); + } + + fs::write(config_path, config_template)?; println!("Config file created at {}", config_path.display()); Ok(()) diff --git a/sea-orm-cli/template/sea-orm.toml b/sea-orm-cli/template/sea-orm.toml index 7019fcb6ba..c5325668c2 100644 --- a/sea-orm-cli/template/sea-orm.toml +++ b/sea-orm-cli/template/sea-orm.toml @@ -1,5 +1,5 @@ [database] -url = "env:DATABASE_URL" # Support env: prefix or direct URL +url = { env = "DATABASE_URL" } # Use an environment variable or provide a direct URL string [migrations] directory = "./migration" # Where migrations live, relative path \ No newline at end of file From 493e2e3ca194c7b734bbe678c9cd709647b4b87b Mon Sep 17 00:00:00 2001 From: Giorgi Merebashvili Date: Thu, 11 Dec 2025 15:50:30 +0400 Subject: [PATCH 11/13] Format the code --- sea-orm-cli/src/cli.rs | 4 ++-- sea-orm-cli/src/commands/config.rs | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index 819ca062b2..a43fdfe9af 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -114,8 +114,8 @@ pub enum ConfigSubcommands { #[command(about = "Initialize config file", display_order = 10)] Init { #[arg(long, help = "Overwrite existing config file")] - force: bool - } + force: bool, + }, } #[derive(Subcommand, PartialEq, Eq, Debug)] diff --git a/sea-orm-cli/src/commands/config.rs b/sea-orm-cli/src/commands/config.rs index 522f76d451..5282819853 100644 --- a/sea-orm-cli/src/commands/config.rs +++ b/sea-orm-cli/src/commands/config.rs @@ -29,9 +29,7 @@ struct Migrations { pub fn run_config_command(command: ConfigSubcommands) -> Result<(), Box> { match command { - ConfigSubcommands::Init{ - force - } => run_config_init(force), + ConfigSubcommands::Init { force } => run_config_init(force), } } @@ -99,7 +97,7 @@ fn run_config_init(force: bool) -> Result<(), Box> { if config_path.exists() && !force { return Err(Box::new(std::io::Error::new( std::io::ErrorKind::AlreadyExists, - "A sea-orm.toml file already exists, use --force to rewrite it" + "A sea-orm.toml file already exists, use --force to rewrite it", ))); } From 8c353c50d2b20bb06d3ef0d10e9b7661b8bfd932 Mon Sep 17 00:00:00 2001 From: Giorgi Merebashvili Date: Thu, 11 Dec 2025 16:37:52 +0400 Subject: [PATCH 12/13] Move config module to cli feature flag --- sea-orm-cli/src/commands/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sea-orm-cli/src/commands/mod.rs b/sea-orm-cli/src/commands/mod.rs index 5bfcb0a166..81884e5a7c 100644 --- a/sea-orm-cli/src/commands/mod.rs +++ b/sea-orm-cli/src/commands/mod.rs @@ -1,12 +1,16 @@ use std::fmt::Display; -#[cfg(feature = "codegen")] +#[cfg(feature = "cli")] pub mod config; + +#[cfg(feature = "codegen")] pub mod generate; pub mod migrate; -#[cfg(feature = "codegen")] +#[cfg(feature = "cli")] pub use config::*; + +#[cfg(feature = "codegen")] pub use generate::*; pub use migrate::*; From f58e148f1283997d944442cf41950d0b3c7ca24d Mon Sep 17 00:00:00 2001 From: Giorgi Merebashvili Date: Thu, 11 Dec 2025 16:50:31 +0400 Subject: [PATCH 13/13] Format .toml files --- sea-orm-cli/Cargo.toml | 7 +++++-- sea-orm-cli/template/sea-orm.toml | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index 7f020518a8..c70ff9d10f 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -52,19 +52,22 @@ sea-schema = { version = "0.17.0-rc.1", default-features = false, features = [ "writer", "probe", ], optional = true } +serde = { version = "1.0.228", features = ["derive"], optional = true } sqlx = { version = "0.8.4", default-features = false, optional = true } tokio = { version = "1.38.2", default-features = false, features = [ "rt-multi-thread", "macros", ], optional = true } +toml = { version = "0.9.8", default-features = false, features = [ + "parse", + "serde", +], optional = true } tracing = { version = "0.1", default-features = false } tracing-subscriber = { version = "0.3.17", default-features = false, features = [ "env-filter", "fmt", ] } url = { version = "2.2", default-features = false } -toml = { version = "0.9.8", default-features = false, features = ["parse", "serde"], optional = true } -serde = { version = "1.0.228", features = ["derive"], optional = true } [dev-dependencies] smol = "1.2.5" diff --git a/sea-orm-cli/template/sea-orm.toml b/sea-orm-cli/template/sea-orm.toml index c5325668c2..38ab096532 100644 --- a/sea-orm-cli/template/sea-orm.toml +++ b/sea-orm-cli/template/sea-orm.toml @@ -1,5 +1,5 @@ [database] -url = { env = "DATABASE_URL" } # Use an environment variable or provide a direct URL string +url = { env = "DATABASE_URL" } # Use an environment variable or provide a direct URL string [migrations] -directory = "./migration" # Where migrations live, relative path \ No newline at end of file +directory = "./migration" # Where migrations live, relative path