Skip to content

Commit a35a6ab

Browse files
authored
feat(cli): kuba show now support -o/--output flag (#47)
Supoorted values: - dotenv (default) - json - shell (can be sourced with `source <(kuba show -o shell --env prod)
1 parent bce2942 commit a35a6ab

File tree

2 files changed

+142
-2
lines changed

2 files changed

+142
-2
lines changed

cmd/kuba/show.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package kuba
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67
"regexp"
78
"sort"
@@ -17,6 +18,7 @@ var (
1718
showEnvironment string
1819
showConfigFile string
1920
showSensitive bool
21+
showOutput string
2022
)
2123

2224
const (
@@ -62,6 +64,7 @@ func init() {
6264
showCmd.Flags().StringVarP(&showEnvironment, "env", "e", "default", "Environment to use (default: default). Provide without value to list available environments.")
6365
showCmd.Flags().StringVarP(&showConfigFile, "config", "c", "", "Path to kuba.yaml configuration file")
6466
showCmd.Flags().BoolVar(&showSensitive, "sensitive", false, "Redact sensitive values")
67+
showCmd.Flags().StringVarP(&showOutput, "output", "o", "dotenv", "Output format: dotenv (default), json, shell")
6568
envFlag := showCmd.Flags().Lookup("env")
6669
if envFlag != nil {
6770
envFlag.NoOptDefVal = showListEnvironmentsValue
@@ -132,13 +135,33 @@ func runShowCommand(patterns []string, listEnvironments bool) error {
132135
filteredSecrets := filterSecrets(secrets, patterns)
133136
logger.Debug("Filtered secrets", "original_count", len(secrets), "filtered_count", len(filteredSecrets))
134137

135-
// Display secrets
138+
// Prepare secrets for output
139+
displaySecrets := make(map[string]string, len(filteredSecrets))
136140
for key, value := range filteredSecrets {
137141
displayValue := value
138142
if showSensitive {
139143
displayValue = maskSecret(value)
140144
}
141-
fmt.Printf("%s=%s\n", key, displayValue)
145+
displaySecrets[key] = displayValue
146+
}
147+
148+
switch showOutput {
149+
case "dotenv":
150+
for _, key := range getSortedKeys(displaySecrets) {
151+
fmt.Printf("%s=%s\n", key, displaySecrets[key])
152+
}
153+
case "shell":
154+
for _, key := range getSortedKeys(displaySecrets) {
155+
fmt.Printf("export %s=%s\n", key, displaySecrets[key])
156+
}
157+
case "json":
158+
payload, err := json.MarshalIndent(displaySecrets, "", " ")
159+
if err != nil {
160+
return fmt.Errorf("failed to format secrets as json: %w", err)
161+
}
162+
fmt.Println(string(payload))
163+
default:
164+
return fmt.Errorf("invalid output format '%s': must be one of: dotenv, json, shell", showOutput)
142165
}
143166

144167
return nil
@@ -193,3 +216,12 @@ func getSortedEnvironmentNames(cfg *config.KubaConfig) []string {
193216
sort.Strings(names)
194217
return names
195218
}
219+
220+
func getSortedKeys(values map[string]string) []string {
221+
keys := make([]string, 0, len(values))
222+
for key := range values {
223+
keys = append(keys, key)
224+
}
225+
sort.Strings(keys)
226+
return keys
227+
}

cmd/kuba/show_test.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package kuba
22

33
import (
4+
"encoding/json"
45
"io"
56
"os"
67
"strings"
@@ -15,6 +16,7 @@ func TestRunShowCommandListsEnvironments(t *testing.T) {
1516
showEnvironment = "default"
1617
showConfigFile = ""
1718
showSensitive = false
19+
showOutput = "dotenv"
1820
})
1921

2022
tmpFile, err := os.CreateTemp("", "kuba-show-*.yaml")
@@ -66,6 +68,7 @@ func TestRunShowCommandUsesProvidedEnvironment(t *testing.T) {
6668
showEnvironment = "default"
6769
showConfigFile = ""
6870
showSensitive = false
71+
showOutput = "dotenv"
6972
})
7073

7174
tmpFile, err := os.CreateTemp("", "kuba-show-*.yaml")
@@ -117,6 +120,7 @@ func TestRunShowCommandConsumesArgWhenEnvFlagNoOptSet(t *testing.T) {
117120
showEnvironment = "default"
118121
showConfigFile = ""
119122
showSensitive = false
123+
showOutput = "dotenv"
120124
})
121125

122126
tmpFile, err := os.CreateTemp("", "kuba-show-*.yaml")
@@ -162,3 +166,107 @@ staging:
162166
output := strings.TrimSpace(string(outputBytes))
163167
assert.Equal(t, "BAR=bar", output)
164168
}
169+
170+
func TestRunShowCommandOutputsJSON(t *testing.T) {
171+
t.Cleanup(func() {
172+
showEnvironment = "default"
173+
showConfigFile = ""
174+
showSensitive = false
175+
showOutput = "dotenv"
176+
})
177+
178+
tmpFile, err := os.CreateTemp("", "kuba-show-*.yaml")
179+
require.NoError(t, err)
180+
t.Cleanup(func() { _ = os.Remove(tmpFile.Name()) })
181+
182+
configContent := `
183+
default:
184+
provider: local
185+
env:
186+
FOO:
187+
value: foo
188+
BAR:
189+
value: bar
190+
`
191+
_, err = tmpFile.WriteString(configContent)
192+
require.NoError(t, err)
193+
require.NoError(t, tmpFile.Close())
194+
195+
showEnvironment = "default"
196+
showConfigFile = tmpFile.Name()
197+
showOutput = "json"
198+
199+
originalStdout := os.Stdout
200+
r, w, err := os.Pipe()
201+
require.NoError(t, err)
202+
os.Stdout = w
203+
204+
runErr := runShowCommand(nil, false)
205+
require.NoError(t, runErr)
206+
207+
require.NoError(t, w.Close())
208+
os.Stdout = originalStdout
209+
210+
outputBytes, err := io.ReadAll(r)
211+
require.NoError(t, err)
212+
require.NoError(t, r.Close())
213+
214+
var output map[string]string
215+
require.NoError(t, json.Unmarshal(outputBytes, &output))
216+
217+
assert.Equal(t, map[string]string{
218+
"BAR": "bar",
219+
"FOO": "foo",
220+
}, output)
221+
}
222+
223+
func TestRunShowCommandOutputsShell(t *testing.T) {
224+
t.Cleanup(func() {
225+
showEnvironment = "default"
226+
showConfigFile = ""
227+
showSensitive = false
228+
showOutput = "dotenv"
229+
})
230+
231+
tmpFile, err := os.CreateTemp("", "kuba-show-*.yaml")
232+
require.NoError(t, err)
233+
t.Cleanup(func() { _ = os.Remove(tmpFile.Name()) })
234+
235+
configContent := `
236+
default:
237+
provider: local
238+
env:
239+
FOO:
240+
value: foo
241+
BAR:
242+
value: bar
243+
`
244+
_, err = tmpFile.WriteString(configContent)
245+
require.NoError(t, err)
246+
require.NoError(t, tmpFile.Close())
247+
248+
showEnvironment = "default"
249+
showConfigFile = tmpFile.Name()
250+
showOutput = "shell"
251+
252+
originalStdout := os.Stdout
253+
r, w, err := os.Pipe()
254+
require.NoError(t, err)
255+
os.Stdout = w
256+
257+
runErr := runShowCommand(nil, false)
258+
require.NoError(t, runErr)
259+
260+
require.NoError(t, w.Close())
261+
os.Stdout = originalStdout
262+
263+
outputBytes, err := io.ReadAll(r)
264+
require.NoError(t, err)
265+
require.NoError(t, r.Close())
266+
267+
output := strings.Split(strings.TrimSpace(string(outputBytes)), "\n")
268+
assert.Equal(t, []string{
269+
"export BAR=bar",
270+
"export FOO=foo",
271+
}, output)
272+
}

0 commit comments

Comments
 (0)