@@ -18,6 +18,7 @@ pub struct GlobalConfig {
1818 pub state_dir : PathBuf ,
1919 pub default_interval : Duration ,
2020 pub max_repo_concurrency : usize ,
21+ pub shell : String ,
2122 pub git_bin : String ,
2223 pub indexer_bin : String ,
2324 pub indexer_args : Vec < String > ,
@@ -61,6 +62,7 @@ struct RawGlobalConfig {
6162 state_dir : Option < PathBuf > ,
6263 default_interval : Option < String > ,
6364 max_repo_concurrency : Option < usize > ,
65+ shell : Option < String > ,
6466 git_bin : Option < String > ,
6567 indexer_bin : Option < String > ,
6668 #[ serde( default ) ]
@@ -125,12 +127,16 @@ impl AppConfig {
125127
126128 let max_repo_concurrency = raw. global . max_repo_concurrency . unwrap_or ( 1 ) . max ( 1 ) ;
127129
130+ let shell = raw. global . shell . unwrap_or_else ( || "sh" . to_string ( ) ) ;
128131 let git_bin = raw. global . git_bin . unwrap_or_else ( || "git" . to_string ( ) ) ;
129132 let indexer_bin = raw
130133 . global
131134 . indexer_bin
132135 . unwrap_or_else ( || "pointer-indexer" . to_string ( ) ) ;
133136
137+ if shell. trim ( ) . is_empty ( ) {
138+ bail ! ( "global.shell must not be empty" ) ;
139+ }
134140 if git_bin. trim ( ) . is_empty ( ) {
135141 bail ! ( "global.git_bin must not be empty" ) ;
136142 }
@@ -142,6 +148,7 @@ impl AppConfig {
142148 state_dir,
143149 default_interval,
144150 max_repo_concurrency,
151+ shell,
145152 git_bin,
146153 indexer_bin,
147154 indexer_args : raw. global . indexer_args ,
@@ -331,9 +338,26 @@ mod tests {
331338 assert_eq ! ( cfg. repos. len( ) , 1 ) ;
332339 assert_eq ! ( cfg. repos[ 0 ] . interval, Duration :: from_secs( 300 ) ) ;
333340 assert_eq ! ( cfg. global. max_repo_concurrency, 1 ) ;
341+ assert_eq ! ( cfg. global. shell, "sh" ) ;
334342 assert ! ( cfg. global. indexer_args. is_empty( ) ) ;
335343 }
336344
345+ #[ test]
346+ fn rejects_empty_global_shell ( ) {
347+ let raw = r#"
348+ [global]
349+ shell = " "
350+
351+ [[repo]]
352+ name = "foo"
353+ url = "git@example.com:foo.git"
354+ branches = ["main"]
355+ "# ;
356+ let parsed: FileConfig = toml:: from_str ( raw) . expect ( "parse config" ) ;
357+ let err = AppConfig :: from_raw ( parsed) . expect_err ( "should fail" ) ;
358+ assert ! ( err. to_string( ) . contains( "global.shell" ) ) ;
359+ }
360+
337361 #[ test]
338362 fn rejects_zero_duration ( ) {
339363 let raw = r#"
0 commit comments