Repository: rsteube/carapace (go)

Issue:
Context: add Env
- add `Env []string` (exec.Cmd style) to `Context` to avoid usage of`os.Setenv`
- add `Context.Setenv(string, string)` for convenience
- ensure empty list exists in `Invoke`
- use it in `ActionExecCommand`

Pull request description:
context: added env
fix #438


Patch:
diff --git a/action.go b/action.go
index 2a44c94..7fa4ec0 100644
--- a/action.go
+++ b/action.go
@@ -35,6 +35,17 @@ type Context struct {
 	Args []string
 	// Parts contains the splitted CallbackValue during an ActionMultiParts (exclusive the part currently being completed)
 	Parts []string
+	// Env contains environment variables for current context (implicitly passed to `exec.Cmd` during ActionExecCommand)
+	Env []string
+}
+
+// Setenv sets the value of the environment variable named by the key.
+func (c Context) Setenv(key, value string) Context {
+	if c.Env == nil {
+		c.Env = []string{}
+	}
+	c.Env = append(c.Env, fmt.Sprintf("%v=%v", key, value))
+	return c
 }
 
 // Cache cashes values of a CompletionCallback for given duration and keys
@@ -65,6 +76,9 @@ func (a Action) Invoke(c Context) InvokedAction {
 	if c.Args == nil {
 		c.Args = []string{}
 	}
+	if c.Env == nil {
+		c.Env = []string{}
+	}
 	if c.Parts == nil {
 		c.Parts = []string{}
 	}
diff --git a/defaultActions.go b/defaultActions.go
index c93aa64..bafeae9 100644
--- a/defaultActions.go
+++ b/defaultActions.go
@@ -32,6 +32,7 @@ func ActionExecCommand(name string, arg ...string) func(f func(output []byte) Ac
 		return ActionCallback(func(c Context) Action {
 			var stdout, stderr bytes.Buffer
 			cmd := exec.Command(name, arg...)
+			cmd.Env = c.Env
 			cmd.Stdout = &stdout
 			cmd.Stderr = &stderr
 			if err := cmd.Run(); err != nil {


Test patch:
diff --git a/carapace_test.go b/carapace_test.go
index c101062..a2c10ae 100644
--- a/carapace_test.go
+++ b/carapace_test.go
@@ -77,6 +77,7 @@ func TestContext(t *testing.T) {
 		CallbackValue: "",
 		Args:          []string{},
 		Parts:         []string{},
+		Env:           []string{},
 	},
 		"")
 
@@ -84,6 +85,7 @@ func TestContext(t *testing.T) {
 		CallbackValue: "",
 		Args:          []string{"pos1"},
 		Parts:         []string{},
+		Env:           []string{},
 	},
 		"pos1", "")
 
@@ -91,6 +93,7 @@ func TestContext(t *testing.T) {
 		CallbackValue: "po",
 		Args:          []string{"pos1", "pos2"},
 		Parts:         []string{},
+		Env:           []string{},
 	},
 		"pos1", "pos2", "po")
 
@@ -98,6 +101,7 @@ func TestContext(t *testing.T) {
 		CallbackValue: "",
 		Args:          []string{},
 		Parts:         []string{},
+		Env:           []string{},
 	},
 		"--multiparts", "")
 
@@ -105,6 +109,7 @@ func TestContext(t *testing.T) {
 		CallbackValue: "fir",
 		Args:          []string{},
 		Parts:         []string{},
+		Env:           []string{},
 	},
 		"--multiparts", "fir")
 
@@ -112,6 +117,7 @@ func TestContext(t *testing.T) {
 		CallbackValue: "seco",
 		Args:          []string{"pos1"},
 		Parts:         []string{"first"},
+		Env:           []string{},
 	},
 		"pos1", "--multiparts", "first,seco")
 
@@ -119,6 +125,7 @@ func TestContext(t *testing.T) {
 		CallbackValue: "pos",
 		Args:          []string{},
 		Parts:         []string{},
+		Env:           []string{},
 	},
 		"pos")
 
@@ -126,6 +133,7 @@ func TestContext(t *testing.T) {
 		CallbackValue: "sec",
 		Args:          []string{},
 		Parts:         []string{"first"},
+		Env:           []string{},
 	},
 		"first:sec")
 
@@ -133,6 +141,7 @@ func TestContext(t *testing.T) {
 		CallbackValue: "thi",
 		Args:          []string{"first:second"},
 		Parts:         []string{},
+		Env:           []string{},
 	},
 		"first:second", "thi")
 }
diff --git a/defaultActions_test.go b/defaultActions_test.go
index 7752bfe..e93bebe 100644
--- a/defaultActions_test.go
+++ b/defaultActions_test.go
@@ -1,6 +1,7 @@
 package carapace
 
 import (
+	"strings"
 	"testing"
 
 	"github.com/spf13/cobra"
@@ -36,3 +37,26 @@ func TestActionFlags(t *testing.T) {
 	a := actionFlags(cmd).Invoke(Context{CallbackValue: "-a"})
 	assertEqual(t, ActionValuesDescribed("b", "").NoSpace().Invoke(Context{}).Prefix("-a"), a)
 }
+
+func TestActionExecCommandEnv(t *testing.T) {
+	ActionExecCommand("env")(func(output []byte) Action {
+		lines := strings.Split(string(output), "\n")
+		for _, line := range lines {
+			if strings.Contains(line, "carapace_TestActionExecCommand") {
+				t.Error("should not contain env carapace_TestActionExecCommand")
+			}
+		}
+		return ActionValues()
+	}).Invoke(Context{})
+
+	ActionExecCommand("env")(func(output []byte) Action {
+		lines := strings.Split(string(output), "\n")
+		for _, line := range lines {
+			if line == "carapace_TestActionExecCommand=test" {
+				return ActionValues()
+			}
+		}
+		t.Error("should contain env carapace_TestActionExecCommand=test")
+		return ActionValues()
+	}).Invoke(Context{}.Setenv("carapace_TestActionExecCommand", "test"))
+}