@@ -2,8 +2,8 @@ use crate::daemon::RunOptions;
22use crate :: ipc:: client:: IpcClient ;
33use crate :: pitchfork_toml:: PitchforkToml ;
44use crate :: Result ;
5- use miette:: { ensure, IntoDiagnostic } ;
6- use std:: collections :: HashSet ;
5+ use miette:: ensure;
6+ use std:: sync :: Arc ;
77
88/// Starts a daemon from a pitchfork.toml file
99#[ derive( Debug , clap:: Args ) ]
@@ -34,86 +34,131 @@ impl Start {
3434 "At least one daemon ID must be provided"
3535 ) ;
3636 let pt = PitchforkToml :: all_merged ( ) ;
37- let ipc = IpcClient :: connect ( true ) . await ?;
37+ let ipc = Arc :: new ( IpcClient :: connect ( true ) . await ?) ;
3838 let disabled_daemons = ipc. get_disabled_daemons ( ) . await ?;
39- let active_daemons: HashSet < String > = ipc
40- . active_daemons ( )
41- . await ?
42- . into_iter ( )
43- . map ( |d| d. id )
44- . collect ( ) ;
4539 let ids = if self . all {
4640 pt. daemons . keys ( ) . cloned ( ) . collect ( )
4741 } else {
4842 self . id . clone ( )
4943 } ;
50- let mut any_failed = false ;
51- let mut last_exit_code = 0 ;
44+ // launch all tasks concurrently
45+ let mut tasks = Vec :: new ( ) ;
5246
53- for id in & ids {
54- if disabled_daemons. contains ( id) {
47+ for id in ids {
48+ if disabled_daemons. contains ( & id) {
5549 warn ! ( "Daemon {} is disabled" , id) ;
5650 continue ;
5751 }
58- if !self . force && active_daemons. contains ( id) {
59- warn ! ( "Daemon {} is already running" , id) ;
60- continue ;
61- }
62- let daemon = pt. daemons . get ( id) ;
63- if let Some ( daemon) = daemon {
64- info ! ( "Starting daemon {}" , id) ;
65- let start_time = chrono:: Local :: now ( ) ;
66- let cmd = shell_words:: split ( & daemon. run ) . into_diagnostic ( ) ?;
67- let ( started, exit_code) = ipc
52+
53+ let daemon_data = match pt. daemons . get ( & id) {
54+ Some ( d) => {
55+ let run = d. run . clone ( ) ;
56+ let auto_stop = d
57+ . auto
58+ . contains ( & crate :: pitchfork_toml:: PitchforkTomlAuto :: Stop ) ;
59+ let dir = d
60+ . path
61+ . as_ref ( )
62+ . and_then ( |p| p. parent ( ) )
63+ . map ( |p| p. to_path_buf ( ) )
64+ . unwrap_or_default ( ) ;
65+ let cron_schedule = d. cron . as_ref ( ) . map ( |c| c. schedule . clone ( ) ) ;
66+ let cron_retrigger = d. cron . as_ref ( ) . map ( |c| c. retrigger ) ;
67+ let retry = d. retry ;
68+ let ready_delay = d. ready_delay ;
69+ let ready_output = d. ready_output . clone ( ) ;
70+
71+ (
72+ run,
73+ auto_stop,
74+ dir,
75+ cron_schedule,
76+ cron_retrigger,
77+ retry,
78+ ready_delay,
79+ ready_output,
80+ )
81+ }
82+ None => {
83+ warn ! ( "Daemon {} not found" , id) ;
84+ continue ;
85+ }
86+ } ;
87+
88+ let (
89+ run,
90+ auto_stop,
91+ dir,
92+ cron_schedule,
93+ cron_retrigger,
94+ retry,
95+ ready_delay,
96+ ready_output,
97+ ) = daemon_data;
98+
99+ let ipc_clone = ipc. clone ( ) ;
100+ let shell_pid = self . shell_pid ;
101+ let force = self . force ;
102+ let delay = self . delay ;
103+ let output = self . output . clone ( ) ;
104+
105+ let task = tokio:: spawn ( async move {
106+ let cmd = match shell_words:: split ( & run) {
107+ Ok ( c) => c,
108+ Err ( e) => {
109+ error ! ( "Failed to parse command for daemon {}: {}" , id, e) ;
110+ return Some ( 1 ) ;
111+ }
112+ } ;
113+
114+ match ipc_clone
68115 . run ( RunOptions {
69116 id : id. clone ( ) ,
70117 cmd,
71- shell_pid : self . shell_pid ,
72- force : self . force ,
73- autostop : daemon
74- . auto
75- . contains ( & crate :: pitchfork_toml:: PitchforkTomlAuto :: Stop ) ,
76- dir : daemon
77- . path
78- . as_ref ( )
79- . unwrap ( )
80- . parent ( )
81- . map ( |p| p. to_path_buf ( ) )
82- . unwrap_or_default ( ) ,
83- cron_schedule : daemon. cron . as_ref ( ) . map ( |c| c. schedule . clone ( ) ) ,
84- cron_retrigger : daemon. cron . as_ref ( ) . map ( |c| c. retrigger ) ,
85- retry : daemon. retry ,
118+ shell_pid,
119+ force,
120+ autostop : auto_stop,
121+ dir,
122+ cron_schedule,
123+ cron_retrigger,
124+ retry,
86125 retry_count : 0 ,
87- ready_delay : self . delay . or ( daemon . ready_delay ) . or ( Some ( 3 ) ) ,
88- ready_output : self . output . clone ( ) . or ( daemon . ready_output . clone ( ) ) ,
126+ ready_delay : delay. or ( ready_delay) . or ( Some ( 3 ) ) ,
127+ ready_output : output. or ( ready_output) ,
89128 wait_ready : true ,
90129 } )
91- . await ?;
92- if !started. is_empty ( ) {
93- info ! ( "started {}" , started. join( ", " ) ) ;
130+ . await
131+ {
132+ Ok ( ( _started, exit_code) ) => exit_code,
133+ Err ( e) => {
134+ error ! ( "Failed to start daemon {}: {}" , id, e) ;
135+ Some ( 1 )
136+ }
94137 }
95- if let Some ( code ) = exit_code {
96- any_failed = true ;
97- last_exit_code = code ;
98- error ! ( "daemon {} failed with exit code {}" , id , code ) ;
138+ } ) ;
139+
140+ tasks . push ( task ) ;
141+ }
99142
100- // Print logs from the time we started this specific daemon
101- if let Err ( e) =
102- crate :: cli:: logs:: print_logs_for_time_range ( id, start_time, None )
103- {
104- error ! ( "Failed to print logs: {}" , e) ;
143+ // wait for all tasks to complete
144+ let mut any_failed = false ;
145+
146+ for task in tasks {
147+ match task. await {
148+ Ok ( exit_code) => {
149+ if exit_code. is_some ( ) {
150+ any_failed = true ;
105151 }
106152 }
107- } else {
108- warn ! ( "Daemon {} not found" , id) ;
153+ Err ( e) => {
154+ error ! ( "Task panicked: {}" , e) ;
155+ any_failed = true ;
156+ }
109157 }
110158 }
111159
112160 if any_failed {
113- if last_exit_code != 0 {
114- error ! ( "Process exited with code {}" , last_exit_code) ;
115- }
116- std:: process:: exit ( last_exit_code) ;
161+ std:: process:: exit ( 1 ) ;
117162 }
118163 Ok ( ( ) )
119164 }
0 commit comments