@@ -28,6 +28,7 @@ use tokio_postgres::Client;
2828/// * `drop_existing` - Drop existing databases on target before copying
2929/// * `enable_sync` - Set up continuous logical replication after snapshot (default: true)
3030/// * `allow_resume` - Resume from checkpoint if available (default: true)
31+ /// * `force_local` - If true, --local was explicitly set (fail instead of fallback to remote)
3132///
3233/// # Returns
3334///
@@ -58,8 +59,9 @@ use tokio_postgres::Client;
5859/// false,
5960/// ReplicationFilter::empty(),
6061/// false,
61- /// true, // Enable continuous replication
62- /// true // Allow resume
62+ /// true, // Enable continuous replication
63+ /// true, // Allow resume
64+ /// false, // Not forcing local execution
6365/// ).await?;
6466///
6567/// // Snapshot only (no continuous replication)
@@ -69,12 +71,14 @@ use tokio_postgres::Client;
6971/// true,
7072/// ReplicationFilter::empty(),
7173/// false,
72- /// false, // Disable continuous replication
73- /// true // Allow resume
74+ /// false, // Disable continuous replication
75+ /// true, // Allow resume
76+ /// true, // Force local execution (--local flag)
7477/// ).await?;
7578/// # Ok(())
7679/// # }
7780/// ```
81+ #[ allow( clippy:: too_many_arguments) ]
7882pub async fn init (
7983 source_url : & str ,
8084 target_url : & str ,
@@ -83,6 +87,7 @@ pub async fn init(
8387 drop_existing : bool ,
8488 enable_sync : bool ,
8589 allow_resume : bool ,
90+ force_local : bool ,
8691) -> Result < ( ) > {
8792 tracing:: info!( "Starting initial replication..." ) ;
8893
@@ -94,6 +99,44 @@ pub async fn init(
9499 crate :: SourceType :: PostgreSQL => {
95100 // PostgreSQL to PostgreSQL replication (existing logic below)
96101 tracing:: info!( "Source type: PostgreSQL" ) ;
102+
103+ // Run pre-flight checks before any destructive operations
104+ tracing:: info!( "Running pre-flight checks..." ) ;
105+
106+ let databases = filter. include_databases ( ) . map ( |v| v. to_vec ( ) ) ;
107+ let preflight_result = crate :: preflight:: run_preflight_checks (
108+ source_url,
109+ target_url,
110+ databases. as_deref ( ) ,
111+ )
112+ . await ?;
113+
114+ preflight_result. print ( ) ;
115+
116+ if !preflight_result. all_passed ( ) {
117+ // Check if we can auto-fallback to remote
118+ if preflight_result. tool_version_incompatible
119+ && crate :: utils:: is_serendb_target ( target_url)
120+ && !force_local
121+ {
122+ println ! ( ) ;
123+ tracing:: info!( "Tool version incompatible. Switching to SerenAI cloud execution..." ) ;
124+ // Return special error that main.rs catches to trigger remote
125+ bail ! ( "PREFLIGHT_FALLBACK_TO_REMOTE" ) ;
126+ }
127+
128+ // Cannot auto-fallback
129+ if force_local {
130+ bail ! (
131+ "Pre-flight checks failed. Cannot continue with --local flag.\n \
132+ Fix the issues above or remove --local to allow remote execution."
133+ ) ;
134+ }
135+
136+ bail ! ( "Pre-flight checks failed. Fix the issues above and retry." ) ;
137+ }
138+
139+ println ! ( ) ;
97140 }
98141 crate :: SourceType :: SQLite => {
99142 // SQLite to PostgreSQL migration (simpler path)
@@ -1106,7 +1149,7 @@ mod tests {
11061149
11071150 // Skip confirmation for automated tests, disable sync to keep test simple
11081151 let filter = crate :: filters:: ReplicationFilter :: empty ( ) ;
1109- let result = init ( & source, & target, true , filter, false , false , true ) . await ;
1152+ let result = init ( & source, & target, true , filter, false , false , true , false ) . await ;
11101153 assert ! ( result. is_ok( ) ) ;
11111154 }
11121155
0 commit comments