@@ -15,9 +15,11 @@ package cmd
1515
1616import (
1717 "bufio"
18+ "errors"
1819 "fmt"
1920 "io"
2021 "os"
22+ "os/exec"
2123 "path/filepath"
2224 "runtime"
2325 "slices"
@@ -160,9 +162,9 @@ dapr run --run-file /path/to/directory -k
160162 fmt .Println (print .WhiteBold ("WARNING: no application command found." ))
161163 }
162164
163- daprDirPath , err := standalone .GetDaprRuntimePath (cmdruntime .GetDaprRuntimePath ())
164- if err != nil {
165- print .FailureStatusEvent (os .Stderr , "Failed to get Dapr install directory: %v" , err )
165+ daprDirPath , pathErr := standalone .GetDaprRuntimePath (cmdruntime .GetDaprRuntimePath ())
166+ if pathErr != nil {
167+ print .FailureStatusEvent (os .Stderr , "Failed to get Dapr install directory: %v" , pathErr )
166168 os .Exit (1 )
167169 }
168170
@@ -227,7 +229,7 @@ dapr run --run-file /path/to/directory -k
227229 sharedRunConfig .SchedulerHostAddress = & addr
228230 }
229231 }
230- output , err := runExec . NewOutput ( & standalone.RunConfig {
232+ appConfig := & standalone.RunConfig {
231233 AppID : appID ,
232234 AppChannelAddress : appChannelAddress ,
233235 AppPort : appPort ,
@@ -239,7 +241,8 @@ dapr run --run-file /path/to/directory -k
239241 UnixDomainSocket : unixDomainSocket ,
240242 InternalGRPCPort : internalGRPCPort ,
241243 SharedRunConfig : * sharedRunConfig ,
242- })
244+ }
245+ output , err := runExec .NewOutput (appConfig )
243246 if err != nil {
244247 print .FailureStatusEvent (os .Stderr , err .Error ())
245248 os .Exit (1 )
@@ -280,6 +283,8 @@ dapr run --run-file /path/to/directory -k
280283
281284 output .DaprCMD .Stdout = os .Stdout
282285 output .DaprCMD .Stderr = os .Stderr
286+ // Set process group so sidecar survives when we exec the app process.
287+ setDaprProcessGroupForRun (output .DaprCMD )
283288
284289 err = output .DaprCMD .Start ()
285290 if err != nil {
@@ -355,53 +360,26 @@ dapr run --run-file /path/to/directory -k
355360 return
356361 }
357362
358- stdErrPipe , pipeErr := output .AppCMD .StderrPipe ()
359- if pipeErr != nil {
360- print .FailureStatusEvent (os .Stderr , "Error creating stderr for App: " + err .Error ())
363+ command := args [0 ]
364+ var binary string
365+ binary , err = exec .LookPath (command )
366+ if err != nil {
367+ print .FailureStatusEvent (os .Stderr , fmt .Sprintf ("Failed to find command %s: %v" , command , err ))
361368 appRunning <- false
362369 return
363370 }
364-
365- stdOutPipe , pipeErr := output .AppCMD .StdoutPipe ()
366- if pipeErr != nil {
367- print .FailureStatusEvent (os .Stderr , "Error creating stdout for App: " + err .Error ())
368- appRunning <- false
369- return
371+ env := output .AppCMD .Env
372+ if len (env ) == 0 {
373+ env = os .Environ ()
370374 }
375+ env = append (env , fmt .Sprintf ("DAPR_HTTP_PORT=%d" , output .DaprHTTPPort ))
376+ env = append (env , fmt .Sprintf ("DAPR_GRPC_PORT=%d" , output .DaprGRPCPort ))
371377
372- errScanner := bufio .NewScanner (stdErrPipe )
373- outScanner := bufio .NewScanner (stdOutPipe )
374- go func () {
375- for errScanner .Scan () {
376- fmt .Println (print .Blue ("== APP == " + errScanner .Text ()))
377- }
378- }()
379-
380- go func () {
381- for outScanner .Scan () {
382- fmt .Println (print .Blue ("== APP == " + outScanner .Text ()))
383- }
384- }()
385-
386- err = output .AppCMD .Start ()
387- if err != nil {
388- print .FailureStatusEvent (os .Stderr , err .Error ())
378+ if startErr := startAppProcessInBackground (output , binary , args , env , sigCh ); startErr != nil {
379+ print .FailureStatusEvent (os .Stderr , startErr .Error ())
389380 appRunning <- false
390381 return
391382 }
392-
393- go func () {
394- appErr := output .AppCMD .Wait ()
395-
396- if appErr != nil {
397- output .AppErr = appErr
398- print .FailureStatusEvent (os .Stderr , "The App process exited with error code: %s" , appErr .Error ())
399- } else {
400- print .SuccessStatusEvent (os .Stdout , "Exited App successfully" )
401- }
402- sigCh <- os .Interrupt
403- }()
404-
405383 appRunning <- true
406384 }()
407385
@@ -465,11 +443,16 @@ dapr run --run-file /path/to/directory -k
465443 if output .AppErr != nil {
466444 exitWithError = true
467445 print .FailureStatusEvent (os .Stderr , fmt .Sprintf ("Error exiting App: %s" , output .AppErr ))
468- } else if output .AppCMD != nil && (output .AppCMD .ProcessState == nil || ! output .AppCMD .ProcessState .Exited ()) {
446+ } else if output .AppCMD != nil && output . AppCMD . Process != nil && (output .AppCMD .ProcessState == nil || ! output .AppCMD .ProcessState .Exited ()) {
469447 err = output .AppCMD .Process .Kill ()
470448 if err != nil {
471- exitWithError = true
472- print .FailureStatusEvent (os .Stderr , fmt .Sprintf ("Error exiting App: %s" , err ))
449+ // If the process already exited on its own, treat this as a clean shutdown.
450+ if errors .Is (err , os .ErrProcessDone ) {
451+ print .SuccessStatusEvent (os .Stdout , "Exited App successfully" )
452+ } else {
453+ exitWithError = true
454+ print .FailureStatusEvent (os .Stderr , fmt .Sprintf ("Error exiting App: %s" , err ))
455+ }
473456 } else {
474457 print .SuccessStatusEvent (os .Stdout , "Exited App successfully" )
475458 }
@@ -788,6 +771,13 @@ func startDaprdAndAppProcesses(runConfig *standalone.RunConfig, commandDir strin
788771 return runState , nil
789772 }
790773
774+ if strings .TrimSpace (runConfig .Command [0 ]) == "" {
775+ noCmdErr := errors .New ("exec: no command" )
776+ print .StatusEvent (appErrorWriter , print .LogFailure , "Error starting app process: %s" , noCmdErr .Error ())
777+ _ = killDaprdProcess (runState )
778+ return nil , noCmdErr
779+ }
780+
791781 // Start App process.
792782 go startAppProcess (runConfig , runState , appRunning , sigCh , startErrChan )
793783
@@ -836,7 +826,7 @@ func stopDaprdAndAppProcesses(runState *runExec.RunExec) bool {
836826 if appErr != nil {
837827 exitWithError = true
838828 print .StatusEvent (runState .AppCMD .ErrorWriter , print .LogFailure , "Error exiting App: %s" , appErr )
839- } else if runState .AppCMD .Command != nil && (runState .AppCMD .Command .ProcessState == nil || ! runState .AppCMD .Command .ProcessState .Exited ()) {
829+ } else if runState .AppCMD .Command != nil && runState . AppCMD . Command . Process != nil && (runState .AppCMD .Command .ProcessState == nil || ! runState .AppCMD .Command .ProcessState .Exited ()) {
840830 err = killAppProcess (runState )
841831 if err != nil {
842832 exitWithError = true
@@ -1009,11 +999,20 @@ func killDaprdProcess(runE *runExec.RunExec) error {
1009999
10101000// killAppProcess is used to kill the App process and return error on failure.
10111001func killAppProcess (runE * runExec.RunExec ) error {
1012- if runE .AppCMD .Command == nil {
1002+ if runE .AppCMD .Command == nil || runE .AppCMD .Command .Process == nil {
1003+ return nil
1004+ }
1005+ // Check if the process has already exited on its own.
1006+ if runE .AppCMD .Command .ProcessState != nil && runE .AppCMD .Command .ProcessState .Exited () {
1007+ // Process already exited, no need to kill it.
10131008 return nil
10141009 }
10151010 err := runE .AppCMD .Command .Process .Kill ()
10161011 if err != nil {
1012+ // If the process already exited on its own
1013+ if errors .Is (err , os .ErrProcessDone ) {
1014+ return nil
1015+ }
10171016 print .StatusEvent (runE .DaprCMD .ErrorWriter , print .LogFailure , "Error exiting App: %s" , err )
10181017 return err
10191018 }
0 commit comments