Skip to content

Commit bded9de

Browse files
committed
feat(list): add --format=dotenv to resolve and output secrets as KEY=VALUE
Adds a --format flag to `vx list` with two modes: - table (default): shows Vault path mappings without fetching - dotenv: authenticates with Vault, resolves all secrets, and outputs sorted KEY=VALUE pairs suitable for piping to a .env file
1 parent aa747be commit bded9de

File tree

1 file changed

+52
-2
lines changed

1 file changed

+52
-2
lines changed

cmd/list.go

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,27 @@ import (
77
"github.com/rs/zerolog/log"
88
"github.com/spf13/cobra"
99

10+
"go.dot.industries/vx/internal/config"
1011
"go.dot.industries/vx/internal/resolver"
1112
)
1213

14+
var flagFormat string
15+
1316
func init() {
17+
listCmd.Flags().StringVar(&flagFormat, "format", "table", "output format: table, dotenv")
1418
rootCmd.AddCommand(listCmd)
1519
}
1620

1721
var listCmd = &cobra.Command{
1822
Use: "list",
1923
Short: "List secrets that would be resolved for the current context",
20-
Long: `Shows all secret mappings for the current environment and workspace
21-
without actually fetching them from Vault. Useful for debugging configuration.`,
24+
Long: `Shows all secret mappings for the current environment and workspace.
25+
26+
The default "table" format shows Vault paths without fetching values.
27+
Use --format=dotenv to resolve secrets from Vault and output KEY=VALUE pairs
28+
suitable for piping to a .env file:
29+
30+
vx list --format=dotenv > .env.docker`,
2231
Args: cobra.NoArgs,
2332
RunE: runList,
2433
}
@@ -48,6 +57,18 @@ func runList(cmd *cobra.Command, args []string) error {
4857
Int("defaults", len(merged.Defaults)).
4958
Msg("resolved config")
5059

60+
switch flagFormat {
61+
case "table":
62+
return printTable(merged, env, workspace)
63+
case "dotenv":
64+
return printDotenv(cfg, merged)
65+
default:
66+
return fmt.Errorf("unsupported format %q (use table or dotenv)", flagFormat)
67+
}
68+
}
69+
70+
// printTable shows the human-readable mapping table (no Vault fetch).
71+
func printTable(merged *config.MergedConfig, env string, workspace string) error {
5172
fmt.Printf("Environment: %s\n", env)
5273
if workspace != "" {
5374
fmt.Printf("Workspace: %s\n", workspace)
@@ -77,6 +98,35 @@ func runList(cmd *cobra.Command, args []string) error {
7798
return nil
7899
}
79100

101+
// printDotenv resolves secrets from Vault and outputs KEY=VALUE lines.
102+
func printDotenv(cfg *config.RootConfig, merged *config.MergedConfig) error {
103+
vaultClient, err := authenticatedClient(cfg, merged.Environment)
104+
if err != nil {
105+
return err
106+
}
107+
108+
secrets, err := resolveSecrets(vaultClient, merged)
109+
if err != nil {
110+
return err
111+
}
112+
113+
// Merge: defaults first, secrets override.
114+
all := make(map[string]string, len(merged.Defaults)+len(secrets))
115+
for k, v := range merged.Defaults {
116+
all[k] = v
117+
}
118+
for k, v := range secrets {
119+
all[k] = v
120+
}
121+
122+
names := sortedKeys(all)
123+
for _, name := range names {
124+
fmt.Printf("%s=%s\n", name, all[name])
125+
}
126+
127+
return nil
128+
}
129+
80130
func sortedKeys(m map[string]string) []string {
81131
keys := make([]string, 0, len(m))
82132
for k := range m {

0 commit comments

Comments
 (0)