From f01e8cb33be1036593030ccd9db906df9f203395 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Mon, 19 Dec 2022 20:21:23 -0500 Subject: [PATCH 1/3] add multi command --- cli/packages/cmd/run.go | 61 +++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/cli/packages/cmd/run.go b/cli/packages/cmd/run.go index ec5210b1fd..0b24d34aba 100644 --- a/cli/packages/cmd/run.go +++ b/cli/packages/cmd/run.go @@ -8,6 +8,7 @@ import ( "os" "os/exec" "os/signal" + "runtime" "strings" "syscall" @@ -19,11 +20,10 @@ import ( // runCmd represents the run command var runCmd = &cobra.Command{ + Example: "infisical run --env=prod -- npm run dev", Use: "run [any infisical run command flags] -- [your application start command]", Short: "Used to inject environments variables into your application process", DisableFlagsInUseLine: true, - Example: "infisical run --env=prod -- npm run dev", - Args: cobra.MinimumNArgs(1), PreRun: toggleDebug, Run: func(cmd *cobra.Command, args []string) { envName, err := cmd.Flags().GetString("env") @@ -54,10 +54,14 @@ var runCmd = &cobra.Command{ } if shouldExpandSecrets { - secretsWithSubstitutions := util.SubstituteSecrets(secrets) - execCmd(args[0], args[1:], secretsWithSubstitutions) + secrets = util.SubstituteSecrets(secrets) + } + + if cmd.Flags().Changed("command") { + command := cmd.Flag("command").Value.String() + _ = executeMultipleCommandWithEnvs(command, secrets) } else { - execCmd(args[0], args[1:], secrets) + _ = executeSingleCommandWithEnvs(args, secrets) } }, @@ -68,22 +72,51 @@ func init() { runCmd.Flags().StringP("env", "e", "dev", "Set the environment (dev, prod, etc.) from which your secrets should be pulled from") runCmd.Flags().String("projectId", "", "The project ID from which your secrets should be pulled from") runCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets") + runCmd.Flags().String("command", "", "command to execute (e.g. \"npm install && npm run dev\")") } -// Credit: inspired by AWS Valut -func execCmd(command string, args []string, envs []models.SingleEnvironmentVariable) error { - numberOfSecretsInjected := fmt.Sprintf("\u2713 Injected %v Infisical secrets into your application process successfully", len(envs)) - +// Will execute a single command and pass in the given secrets into the process +func executeSingleCommandWithEnvs(args []string, secrets []models.SingleEnvironmentVariable) error { + command := args[0] + argsForCommand := args[1:] + numberOfSecretsInjected := fmt.Sprintf("\u2713 Injected %v Infisical secrets into your application process successfully", len(secrets)) log.Infof("\x1b[%dm%s\x1b[0m", 32, numberOfSecretsInjected) - log.Debugf("executing command: %s %s \n", command, strings.Join(args, " ")) - log.Debugln("Secrets injected:", envs) + log.Debugf("executing command: %s %s \n", command, strings.Join(argsForCommand, " ")) + log.Debugln("Secrets injected:", secrets) - cmd := exec.Command(command, args...) + cmd := exec.Command(command, argsForCommand...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - cmd.Env = getAllEnvs(envs) + cmd.Env = getAllEnvs(secrets) + + return execCmd(cmd) +} + +func executeMultipleCommandWithEnvs(fullCommand string, secrets []models.SingleEnvironmentVariable) error { + shell := [2]string{"sh", "-c"} + if runtime.GOOS == "windows" { + shell = [2]string{"cmd", "/C"} + } else { + shell[0] = os.Getenv("SHELL") + } + cmd := exec.Command(shell[0], shell[1], fullCommand) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = getAllEnvs(secrets) + + numberOfSecretsInjected := fmt.Sprintf("\u2713 Injected %v Infisical secrets into your application process successfully", len(secrets)) + log.Infof("\x1b[%dm%s\x1b[0m", 32, numberOfSecretsInjected) + log.Debugf("executing command: %s %s %s \n", shell[0], shell[1], fullCommand) + log.Debugln("Secrets injected:", secrets) + + return execCmd(cmd) +} + +// Credit: inspired by AWS Valut +func execCmd(cmd *exec.Cmd) error { sigChannel := make(chan os.Signal, 1) signal.Notify(sigChannel) @@ -100,7 +133,7 @@ func execCmd(command string, args []string, envs []models.SingleEnvironmentVaria if err := cmd.Wait(); err != nil { _ = cmd.Process.Signal(os.Kill) - return fmt.Errorf("Failed to wait for command termination: %v", err) + return fmt.Errorf("failed to wait for command termination: %v", err) } waitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus) From dc9c6b9d131e2c052c61516b698de5d1504c12b3 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Tue, 20 Dec 2022 12:54:41 -0500 Subject: [PATCH 2/3] add chained command support --- cli/packages/cmd/run.go | 44 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/cli/packages/cmd/run.go b/cli/packages/cmd/run.go index 0b24d34aba..7518fe98df 100644 --- a/cli/packages/cmd/run.go +++ b/cli/packages/cmd/run.go @@ -20,11 +20,38 @@ import ( // runCmd represents the run command var runCmd = &cobra.Command{ - Example: "infisical run --env=prod -- npm run dev", + Example: ` + infisical run --env=dev -- npm run dev + infisical run --command "first-command && second-command; more-commands..." + `, Use: "run [any infisical run command flags] -- [your application start command]", Short: "Used to inject environments variables into your application process", DisableFlagsInUseLine: true, PreRun: toggleDebug, + Args: func(cmd *cobra.Command, args []string) error { + // Check if the --command flag has been set + commandFlagSet := cmd.Flags().Changed("command") + + // If the --command flag has been set, check if a value was provided + if commandFlagSet { + command := cmd.Flag("command").Value.String() + if command == "" { + return fmt.Errorf("you need to provide a command after the flag --command") + } + + // If the --command flag has been set, args should not be provided + if len(args) > 0 { + return fmt.Errorf("you cannot set any arguments after --command flag. --command only takes a string command") + } + } else { + // If the --command flag has not been set, at least one arg should be provided + if len(args) == 0 { + return fmt.Errorf("at least one argument is required after the run command, received %d", len(args)) + } + } + + return nil + }, Run: func(cmd *cobra.Command, args []string) { envName, err := cmd.Flags().GetString("env") if err != nil { @@ -59,9 +86,18 @@ var runCmd = &cobra.Command{ if cmd.Flags().Changed("command") { command := cmd.Flag("command").Value.String() - _ = executeMultipleCommandWithEnvs(command, secrets) + err = executeMultipleCommandWithEnvs(command, secrets) + if err != nil { + log.Errorf("Something went wrong when executing your command [error=%s]", err) + return + } } else { - _ = executeSingleCommandWithEnvs(args, secrets) + err = executeSingleCommandWithEnvs(args, secrets) + if err != nil { + log.Errorf("Something went wrong when executing your command [error=%s]", err) + return + } + return } }, @@ -72,7 +108,7 @@ func init() { runCmd.Flags().StringP("env", "e", "dev", "Set the environment (dev, prod, etc.) from which your secrets should be pulled from") runCmd.Flags().String("projectId", "", "The project ID from which your secrets should be pulled from") runCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets") - runCmd.Flags().String("command", "", "command to execute (e.g. \"npm install && npm run dev\")") + runCmd.Flags().StringP("command", "c", "", "chained commands to execute (e.g. \"npm install && npm run dev; echo ...\")") } // Will execute a single command and pass in the given secrets into the process From b09ae054dd4a5ba728f3073f261476950a3054ae Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Tue, 20 Dec 2022 13:18:20 -0500 Subject: [PATCH 3/3] Add docs for chained commands --- docs/cli/commands/run.mdx | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/docs/cli/commands/run.mdx b/docs/cli/commands/run.mdx index 7fb2076129..2c65ef53e9 100644 --- a/docs/cli/commands/run.mdx +++ b/docs/cli/commands/run.mdx @@ -2,9 +2,25 @@ title: "infisical run" --- -```bash -infisical run [options] -- [your application start command] -``` + + + ```bash + infisical run [options] -- [your application start command] + + # Example + infisical run [options] -- npm run dev + ``` + + + + ```bash + infisical run [options] --command [string command] + + # Example + infisical run [options] --command "npm run bootstrap && npm run dev start; other-bash-command" + ``` + + ## Description @@ -15,5 +31,6 @@ Inject environment variables from the platform into an application process. | Option | Description | Default value | | -------------- | ----------------------------------------------------------------------------------------------------------- | ------------- | | `--env` | Used to set the environment that secrets are pulled from. Accepted values: `dev`, `staging`, `test`, `prod` | `dev` | -| `--projectId` | Used to link a local project to the platform (required only if injecting via the service token method) | `None` | +| `--projectId` | Used to link a local project to the platform (required only if injecting via the service token method) | None | | `--expand` | Parse shell parameter expansions in your secrets (e.g., `${DOMAIN}`) | `true` | +| `--command` | Pass secrets into chained commands (e.g., `"first-command && second-command; more-commands..."`) | None |