Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion internal/encoding/dotenv/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,17 @@ func (Codec) Encode(v map[string]any) ([]byte, error) {
var buf bytes.Buffer

for _, key := range keys {
_, err := buf.WriteString(fmt.Sprintf("%v=%v\n", strings.ToUpper(key), flattened[key]))
var valStr string
switch v := flattened[key].(type) {
case []string:
// Encode string slices as comma-separated values.
// Using fmt's %v would produce "[a b]" which gotenv reads back
// as a single string instead of a slice.
valStr = strings.Join(v, ",")
default:
valStr = fmt.Sprintf("%v", v)
}
_, err := buf.WriteString(fmt.Sprintf("%v=%v\n", strings.ToUpper(key), valStr))
if err != nil {
return nil, err
}
Expand Down
19 changes: 19 additions & 0 deletions internal/encoding/dotenv/codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,22 @@ func TestCodec_Decode(t *testing.T) {
t.Logf("decoding failed as expected: %s", err)
})
}

// TestCodec_Encode_StringSlice verifies that []string values are encoded as
// comma-separated values rather than Go's default bracket notation "[a b]".
//
// dotenv has no array type, so the de-facto convention is CSV (VAL=a,b,c).
// Viper's defaultDecoderConfig uses stringToWeakSliceHookFunc(",") which
// reconstructs []string from a comma-separated string during Unmarshal,
// completing the round-trip.
func TestCodec_Encode_StringSlice(t *testing.T) {
codec := Codec{}

b, err := codec.Encode(map[string]any{
"HOSTS": []string{"host1:10101", "host2:10101"},
})
require.NoError(t, err)

// Must be comma-separated, not bracket notation.
assert.Equal(t, "HOSTS=host1:10101,host2:10101\n", string(b))
}