Skip to content

Commit

Permalink
Merge pull request #142 from DopplerHQ/tom_signals
Browse files Browse the repository at this point in the history
Fix: forward all signals to subprocess
  • Loading branch information
Piccirello authored Dec 16, 2020
2 parents 2463a81 + fab88cc commit 6ff8237
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 13 deletions.
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@ require (
github.com/hashicorp/go-version v1.2.1
github.com/jedib0t/go-pretty v4.3.0+incompatible
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.10 // indirect
github.com/mattn/go-isatty v0.0.12
github.com/mattn/go-runewidth v0.0.5 // indirect
github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5 // indirect
github.com/zalando/go-keyring v0.1.0
go.mongodb.org/mongo-driver v1.1.2 // indirect
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c // indirect
gopkg.in/gookit/color.v1 v1.1.6
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.5 h1:jrGtp51JOKTWgvLFzfG6OtZOJcK2sEnzc/U+zw7TtbA=
github.com/mattn/go-runewidth v0.0.5/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
Expand Down Expand Up @@ -110,6 +112,8 @@ golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c h1:S/FtSvpNLtFBgjTqcKsRpsa6aVsI6iztaz1bQd9BJwE=
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
9 changes: 7 additions & 2 deletions pkg/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/DopplerHQ/cli/pkg/http"
"github.com/DopplerHQ/cli/pkg/models"
"github.com/DopplerHQ/cli/pkg/utils"
"github.com/mattn/go-isatty"
"github.com/spf13/cobra"
"gopkg.in/gookit/color.v1"
)
Expand Down Expand Up @@ -72,6 +73,7 @@ doppler run --command "YOUR_COMMAND && YOUR_OTHER_COMMAND"`,
fallbackOnly := utils.GetBoolFlag(cmd, "fallback-only")
exitOnWriteFailure := !utils.GetBoolFlag(cmd, "no-exit-on-write-failure")
preserveEnv := utils.GetBoolFlag(cmd, "preserve-env")
forwardSignals := utils.GetBoolFlag(cmd, "forward-signals")
localConfig := configuration.LocalConfig(cmd)

utils.RequireValue("token", localConfig.Token.Value)
Expand Down Expand Up @@ -143,9 +145,9 @@ doppler run --command "YOUR_COMMAND && YOUR_OTHER_COMMAND"`,

if cmd.Flags().Changed("command") {
command := cmd.Flag("command").Value.String()
exitCode, err = utils.RunCommandString(command, env, os.Stdin, os.Stdout, os.Stderr)
exitCode, err = utils.RunCommandString(command, env, os.Stdin, os.Stdout, os.Stderr, forwardSignals)
} else {
exitCode, err = utils.RunCommand(args, env, os.Stdin, os.Stdout, os.Stderr)
exitCode, err = utils.RunCommand(args, env, os.Stdin, os.Stdout, os.Stderr, forwardSignals)
}

if err != nil {
Expand Down Expand Up @@ -526,6 +528,8 @@ func init() {
defaultFallbackDir = filepath.Join(configuration.UserConfigDir, "fallback")
controllers.DefaultMetadataDir = defaultFallbackDir

forwardSignals := !isatty.IsTerminal(os.Stdout.Fd())

runCmd.Flags().StringP("project", "p", "", "project (e.g. backend)")
runCmd.Flags().StringP("config", "c", "", "config (e.g. dev)")
runCmd.Flags().String("command", "", "command to execute (e.g. \"echo hi\")")
Expand All @@ -539,6 +543,7 @@ func init() {
runCmd.Flags().Bool("fallback-readonly", false, "disable modifying the fallback file. secrets can still be read from the file.")
runCmd.Flags().Bool("fallback-only", false, "read all secrets directly from the fallback file, without contacting Doppler. secrets will not be updated. (implies --fallback-readonly)")
runCmd.Flags().Bool("no-exit-on-write-failure", false, "do not exit if unable to write the fallback file")
runCmd.Flags().Bool("forward-signals", forwardSignals, "forward signals to the child process (defaults to false when STDOUT is a TTY)")

// deprecated
runCmd.Flags().Bool("silent-exit", false, "disable error output if the supplied command exits non-zero")
Expand Down
25 changes: 16 additions & 9 deletions pkg/utils/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,18 @@ func Cwd() string {
}

// RunCommand runs the specified command
func RunCommand(command []string, env []string, inFile *os.File, outFile *os.File, errFile *os.File) (int, error) {
func RunCommand(command []string, env []string, inFile *os.File, outFile *os.File, errFile *os.File, forwardSignals bool) (int, error) {
cmd := exec.Command(command[0], command[1:]...) // #nosec G204
cmd.Env = env
cmd.Stdin = inFile
cmd.Stdout = outFile
cmd.Stderr = errFile

return execCommand(cmd)
return execCommand(cmd, forwardSignals)
}

// RunCommandString runs the specified command string
func RunCommandString(command string, env []string, inFile *os.File, outFile *os.File, errFile *os.File) (int, error) {
func RunCommandString(command string, env []string, inFile *os.File, outFile *os.File, errFile *os.File, forwardSignals bool) (int, error) {
shell := [2]string{"sh", "-c"}
if IsWindows() {
shell = [2]string{"cmd", "/C"}
Expand All @@ -137,10 +137,10 @@ func RunCommandString(command string, env []string, inFile *os.File, outFile *os
cmd.Stdout = outFile
cmd.Stderr = errFile

return execCommand(cmd)
return execCommand(cmd, forwardSignals)
}

func execCommand(cmd *exec.Cmd) (int, error) {
func execCommand(cmd *exec.Cmd, forwardSignals bool) (int, error) {
// signal handling logic adapted from aws-vault https://github.com/99designs/aws-vault/
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan)
Expand All @@ -149,12 +149,19 @@ func execCommand(cmd *exec.Cmd) (int, error) {
return 1, err
}

// catch and ignore all signals
// no need to manually send them to the subprocess since it's in the same process group
// handle all signals
go func() {
for {
// ignore
<-sigChan
// When running with a TTY, user-generated signals (like SIGINT) are sent to the entire process group.
// If we forward the signal, the child process will end up receiving the signal twice.
if forwardSignals {
// forward to process
sig := <-sigChan
cmd.Process.Signal(sig) // #nosec G104
} else {
// ignore
<-sigChan
}
}
}()

Expand Down

0 comments on commit 6ff8237

Please sign in to comment.