@@ -330,6 +330,14 @@ impl AppCore {
330330 let _ = append_log ( & self . paths . logs , "ERROR" , message) ;
331331 }
332332
333+ pub fn configuration_refresh_failed ( & self , message : & str ) {
334+ let _ = append_log (
335+ & self . paths . logs ,
336+ "ERROR" ,
337+ & format ! ( "startup configuration refresh failed: {message}" ) ,
338+ ) ;
339+ }
340+
333341 pub fn set_usage_listening ( & self , listening : bool ) {
334342 self . usage_listening . store ( listening, Ordering :: Relaxed ) ;
335343 }
@@ -988,7 +996,13 @@ impl AppCore {
988996 . as_file ( )
989997 . sync_all ( )
990998 . map_err ( |error| format ! ( "无法同步配置候选:{error}" ) ) ?;
991- validate_configuration ( candidate. path ( ) , settings, selected, routes) ?;
999+ validate_configuration (
1000+ candidate. path ( ) ,
1001+ settings,
1002+ selected,
1003+ routes,
1004+ ValidationEnvironment :: ManagedConfigOnly ,
1005+ ) ?;
9921006 }
9931007 let _ = backup_file (
9941008 & self . paths . gitconfig ,
@@ -1003,9 +1017,13 @@ impl AppCore {
10031017 }
10041018 }
10051019 if settings. acceleration_enabled && settings. line_mode != LineMode :: Direct {
1006- if let Err ( error) =
1007- validate_configuration ( & self . paths . gitconfig , settings, selected, routes)
1008- {
1020+ if let Err ( error) = validate_configuration (
1021+ & self . paths . gitconfig ,
1022+ settings,
1023+ selected,
1024+ routes,
1025+ ValidationEnvironment :: EffectiveGit ,
1026+ ) {
10091027 self . restore_git_state ( previous. as_deref ( ) , registered_before) ;
10101028 return Err ( format ! ( "写入后的 Git 配置验证失败:{error}" ) ) ;
10111029 }
@@ -1316,11 +1334,18 @@ impl AppCore {
13161334 }
13171335}
13181336
1337+ #[ derive( Clone , Copy ) ]
1338+ enum ValidationEnvironment {
1339+ ManagedConfigOnly ,
1340+ EffectiveGit ,
1341+ }
1342+
13191343fn validate_configuration (
13201344 config_path : & Path ,
13211345 settings : & Settings ,
13221346 selected : Option < & NodeDefinition > ,
13231347 routes : & [ RouteEntry ] ,
1348+ environment : ValidationEnvironment ,
13241349) -> Result < ( ) , String > {
13251350 let node = selected. ok_or_else ( || "没有通过检测的可用节点" . to_string ( ) ) ?;
13261351 let mut checks = Vec :: new ( ) ;
@@ -1363,7 +1388,12 @@ fn validate_configuration(
13631388 }
13641389
13651390 for ( original, expected_fetch) in checks {
1366- let ( fetch, push) = git:: effective_urls ( config_path, & original) ?;
1391+ let ( fetch, push) = match environment {
1392+ ValidationEnvironment :: ManagedConfigOnly => {
1393+ git:: effective_urls_isolated ( config_path, & original)
1394+ }
1395+ ValidationEnvironment :: EffectiveGit => git:: effective_urls ( config_path, & original) ,
1396+ } ?;
13671397 if fetch != expected_fetch {
13681398 return Err ( format ! ( "配置未正确处理 fetch:{original}" ) ) ;
13691399 }
@@ -1807,7 +1837,14 @@ mod tests {
18071837 )
18081838 . unwrap ( ) ;
18091839 atomic_write ( & core. paths . gitconfig , complete. as_bytes ( ) ) . unwrap ( ) ;
1810- validate_configuration ( & core. paths . gitconfig , & settings, Some ( & node) , & routes) . unwrap ( ) ;
1840+ validate_configuration (
1841+ & core. paths . gitconfig ,
1842+ & settings,
1843+ Some ( & node) ,
1844+ & routes,
1845+ ValidationEnvironment :: ManagedConfigOnly ,
1846+ )
1847+ . unwrap ( ) ;
18111848
18121849 let incomplete = git:: build_config (
18131850 & settings,
@@ -1817,11 +1854,107 @@ mod tests {
18171854 )
18181855 . unwrap ( ) ;
18191856 atomic_write ( & core. paths . gitconfig , incomplete. as_bytes ( ) ) . unwrap ( ) ;
1820- let error = validate_configuration ( & core. paths . gitconfig , & settings, Some ( & node) , & routes)
1821- . unwrap_err ( ) ;
1857+ let error = validate_configuration (
1858+ & core. paths . gitconfig ,
1859+ & settings,
1860+ Some ( & node) ,
1861+ & routes,
1862+ ValidationEnvironment :: ManagedConfigOnly ,
1863+ )
1864+ . unwrap_err ( ) ;
18221865 assert ! ( error. contains( "octocat/Hello-World" ) ) ;
18231866 }
18241867
1868+ #[ test]
1869+ fn configuration_update_isolates_the_candidate_but_validates_effective_git_rules ( ) {
1870+ const MARKER : & str = "GITBOOST_VALIDATION_ENVIRONMENT_CHILD" ;
1871+ if rerun_with_isolated_git (
1872+ "core::tests::configuration_update_isolates_the_candidate_but_validates_effective_git_rules" ,
1873+ MARKER ,
1874+ ) {
1875+ return ;
1876+ }
1877+ let directory = tempfile:: tempdir ( ) . unwrap ( ) ;
1878+ let core = AppCore :: new ( directory. path ( ) . to_path_buf ( ) ) . unwrap ( ) ;
1879+ let route = RouteEntry {
1880+ id : "route" . into ( ) ,
1881+ repository_url : "https://github.com/openai/codex.git" . into ( ) ,
1882+ created_at : Utc :: now ( ) ,
1883+ } ;
1884+ let replacement = NodeDefinition {
1885+ id : "replacement" . into ( ) ,
1886+ name : "Replacement" . into ( ) ,
1887+ rewrite_base : "https://new.example/https://github.com/" . into ( ) ,
1888+ enabled : true ,
1889+ built_in : false ,
1890+ } ;
1891+ let settings = Settings {
1892+ acceleration_enabled : true ,
1893+ line_mode : LineMode :: Fixed ,
1894+ fixed_node_id : Some ( replacement. id . clone ( ) ) ,
1895+ current_node_id : Some ( replacement. id . clone ( ) ) ,
1896+ ..Settings :: default ( )
1897+ } ;
1898+ atomic_write_json ( & core. paths . routes , & vec ! [ route. clone( ) ] ) . unwrap ( ) ;
1899+ atomic_write_json ( & core. paths . nodes , & vec ! [ replacement. clone( ) ] ) . unwrap ( ) ;
1900+ atomic_write_json (
1901+ & core. paths . health ,
1902+ & HashMap :: from ( [ (
1903+ replacement. id . clone ( ) ,
1904+ HealthSummary {
1905+ status : NodeStatus :: Available ,
1906+ success_count : 1 ,
1907+ attempt_count : 1 ,
1908+ ..HealthSummary :: default ( )
1909+ } ,
1910+ ) ] ) ,
1911+ )
1912+ . unwrap ( ) ;
1913+ atomic_write_json ( & core. paths . settings , & settings) . unwrap ( ) ;
1914+
1915+ let old_config = git:: build_config (
1916+ & Settings {
1917+ acceleration_enabled : true ,
1918+ line_mode : LineMode :: Fixed ,
1919+ fixed_node_id : Some ( FASTGIT_REWRITE_BASE . into ( ) ) ,
1920+ current_node_id : Some ( FASTGIT_REWRITE_BASE . into ( ) ) ,
1921+ ..Settings :: default ( )
1922+ } ,
1923+ Some ( & NodeDefinition :: fastgit ( ) ) ,
1924+ std:: slice:: from_ref ( & route) ,
1925+ Some ( & core. paths . trace_socket ) ,
1926+ )
1927+ . unwrap ( ) ;
1928+ atomic_write ( & core. paths . gitconfig , old_config. as_bytes ( ) ) . unwrap ( ) ;
1929+ git:: register_include ( & core. paths . gitconfig ) . unwrap ( ) ;
1930+
1931+ core. refresh_registered_configuration ( ) . unwrap ( ) ;
1932+ let ( fetch, push) =
1933+ git:: effective_urls ( & core. paths . gitconfig , & route. repository_url ) . unwrap ( ) ;
1934+ assert_eq ! (
1935+ fetch,
1936+ "https://new.example/https://github.com/openai/codex.git"
1937+ ) ;
1938+ assert_eq ! ( push, route. repository_url) ;
1939+
1940+ let previous_config = fs:: read ( & core. paths . gitconfig ) . unwrap ( ) ;
1941+ let conflict = std:: process:: Command :: new ( "git" )
1942+ . args ( [
1943+ "config" ,
1944+ "--global" ,
1945+ "url.https://push-proxy.example/https://github.com/openai/codex.pushInsteadOf" ,
1946+ "https://github.com/openai/codex" ,
1947+ ] )
1948+ . output ( )
1949+ . unwrap ( ) ;
1950+ assert ! ( conflict. status. success( ) ) ;
1951+ let error = core. refresh_registered_configuration ( ) . unwrap_err ( ) ;
1952+ assert ! ( error. contains( "配置未保持 push 直连" ) ) ;
1953+ assert_eq ! ( fs:: read( & core. paths. gitconfig) . unwrap( ) , previous_config) ;
1954+
1955+ git:: unregister_include ( & core. paths . gitconfig ) . unwrap ( ) ;
1956+ }
1957+
18251958 #[ test]
18261959 fn startup_reconciles_missing_include_and_rolls_back_on_state_write_failure ( ) {
18271960 const MARKER : & str = "GITBOOST_STARTUP_RECONCILE_CHILD" ;
0 commit comments