@@ -11,6 +11,7 @@ import (
1111 "context"
1212 "os"
1313 "os/exec"
14+ "syscall"
1415 "time"
1516
1617 "github.com/pkg/errors"
@@ -25,11 +26,26 @@ type cmd struct {
2526}
2627
2728func commandContext (ctx context.Context , name string , arg ... string ) cmd {
28- // Grab the caller's context and pass a derived one to CommandContext.
29- c := cmd {ctx : ctx }
30- ctx , cancel := context .WithCancel (ctx )
31- c .Cmd = exec .CommandContext (ctx , name , arg ... )
32- c .cancel = cancel
29+ // Create a one-off cancellable context for use by the CommandContext, in
30+ // the event that we have to force a Process.Kill().
31+ ctx2 , cancel := context .WithCancel (context .Background ())
32+
33+ c := cmd {
34+ Cmd : exec .CommandContext (ctx2 , name , arg ... ),
35+ cancel : cancel ,
36+ ctx : ctx ,
37+ }
38+
39+ // Force subprocesses into their own process group, rather than being in the
40+ // same process group as the dep process. Because Ctrl-C sent from a
41+ // terminal will send the signal to the entire currently running process
42+ // group, this allows us to directly manage the issuance of signals to
43+ // subprocesses.
44+ c .Cmd .SysProcAttr = & syscall.SysProcAttr {
45+ Setpgid : true ,
46+ Pgid : 0 ,
47+ }
48+
3349 return c
3450}
3551
@@ -47,6 +63,7 @@ func (c cmd) CombinedOutput() ([]byte, error) {
4763 var b bytes.Buffer
4864 c .Cmd .Stdout = & b
4965 c .Cmd .Stderr = & b
66+
5067 if err := c .Cmd .Start (); err != nil {
5168 return nil , err
5269 }
0 commit comments