Skip to content

Commit 1acf082

Browse files
authored
fix: support environment variables with equals sign and newlines in value (#1977)
1 parent 362630f commit 1acf082

File tree

2 files changed

+49
-18
lines changed

2 files changed

+49
-18
lines changed

internal/execenv/execenv.go

+13-17
Original file line numberDiff line numberDiff line change
@@ -47,30 +47,26 @@ func Generate(plugin plugins.Plugin, callbackEnv map[string]string) (env map[str
4747
expression.Stdout = &stdout
4848
err = expression.Run()
4949

50-
return envMap(stdout.String()), err
51-
}
52-
53-
func envMap(env string) map[string]string {
54-
slice := map[string]string{}
55-
56-
for _, envVar := range strings.Split(env, "\n") {
57-
varValue := strings.Split(envVar, "=")
58-
if len(varValue) == 2 {
59-
slice[varValue[0]] = varValue[1]
60-
}
61-
}
62-
63-
return slice
50+
str := stdout.String()
51+
return SliceToMap(strings.Split(str, "\n")), err
6452
}
6553

6654
// SliceToMap converts an env map to env slice suitable for syscall.Exec
6755
func SliceToMap(env []string) map[string]string {
6856
envMap := map[string]string{}
6957

58+
var previousKey string
59+
7060
for _, envVar := range env {
71-
varValue := strings.Split(envVar, "=")
72-
if len(varValue) >= 2 {
73-
envMap[varValue[0]] = strings.Join(varValue[1:], "=")
61+
varValue := strings.SplitN(envVar, "=", 2)
62+
if len(varValue) == 2 {
63+
// new var=value line
64+
previousKey = varValue[0]
65+
envMap[varValue[0]] = varValue[1]
66+
} else {
67+
// value from variable defined on a previous line, append
68+
val := envMap[previousKey]
69+
envMap[previousKey] = val + "\n" + varValue[0]
7470
}
7571
}
7672

internal/execenv/execenv_test.go

+36-1
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ func TestMergeEnv(t *testing.T) {
4949
}
5050

5151
func TestGenerate(t *testing.T) {
52-
testDataDir := t.TempDir()
5352

5453
t.Run("returns map of environment variables", func(t *testing.T) {
54+
testDataDir := t.TempDir()
5555
conf := config.Config{DataDir: testDataDir}
5656
_, err := repotest.InstallPlugin("dummy_plugin", testDataDir, testPluginName)
5757
assert.Nil(t, err)
@@ -64,6 +64,7 @@ func TestGenerate(t *testing.T) {
6464
})
6565

6666
t.Run("returns error when plugin lacks exec-env callback", func(t *testing.T) {
67+
testDataDir := t.TempDir()
6768
conf := config.Config{DataDir: testDataDir}
6869
_, err := repotest.InstallPlugin("dummy_plugin", testDataDir, testPluginName2)
6970
assert.Nil(t, err)
@@ -73,6 +74,32 @@ func TestGenerate(t *testing.T) {
7374
_, found := env["FOO"]
7475
assert.False(t, found)
7576
})
77+
78+
t.Run("preserves environment variables that contain equals sign in value", func(t *testing.T) {
79+
testDataDir := t.TempDir()
80+
conf := config.Config{DataDir: testDataDir}
81+
_, err := repotest.InstallPlugin("dummy_plugin", testDataDir, testPluginName)
82+
assert.Nil(t, err)
83+
plugin := plugins.New(conf, testPluginName)
84+
assert.Nil(t, repotest.WritePluginCallback(plugin.Dir, "exec-env", "#!/usr/bin/env bash\nexport BAZ=bar"))
85+
env, err := Generate(plugin, map[string]string{"EQUALSTEST": "abc=123"})
86+
assert.Nil(t, err)
87+
assert.Equal(t, "bar", env["BAZ"])
88+
assert.Equal(t, "abc=123", env["EQUALSTEST"])
89+
})
90+
91+
t.Run("preserves environment variables that contain equals sign in value", func(t *testing.T) {
92+
testDataDir := t.TempDir()
93+
conf := config.Config{DataDir: testDataDir}
94+
_, err := repotest.InstallPlugin("dummy_plugin", testDataDir, testPluginName)
95+
assert.Nil(t, err)
96+
plugin := plugins.New(conf, testPluginName)
97+
assert.Nil(t, repotest.WritePluginCallback(plugin.Dir, "exec-env", "#!/usr/bin/env bash\nexport BAZ=bar"))
98+
env, err := Generate(plugin, map[string]string{"EQUALSTEST": "abc\n123"})
99+
assert.Nil(t, err)
100+
assert.Equal(t, "bar", env["BAZ"])
101+
assert.Equal(t, "abc\n123", env["EQUALSTEST"])
102+
})
76103
}
77104

78105
func TestSliceToMap(t *testing.T) {
@@ -92,6 +119,14 @@ func TestSliceToMap(t *testing.T) {
92119
input: []string{"MYVAR=some things = with = in it"},
93120
output: map[string]string{"MYVAR": "some things = with = in it"},
94121
},
122+
{
123+
input: []string{"MYVAR=value\nwith\nnewlines"},
124+
output: map[string]string{"MYVAR": "value\nwith\nnewlines"},
125+
},
126+
{
127+
input: []string{"MYVAR=value", "with", "newlines"},
128+
output: map[string]string{"MYVAR": "value\nwith\nnewlines"},
129+
},
95130
}
96131

97132
for _, tt := range tests {

0 commit comments

Comments
 (0)