From b7709d693bbd23e9ac6c33035bab5492bf238eda Mon Sep 17 00:00:00 2001 From: Andriy Oblivantsev Date: Tue, 12 May 2026 12:27:46 +0100 Subject: [PATCH] feat(envc): add support for converting current process environment to YAML Enhance the `envc convert` command to allow conversion of the current process environment to YAML format. Update README with usage examples and clarify input options. Add tests to ensure correct functionality for environment variable handling. --- .vscode/launch.json | 21 +++++++++++++++++++++ README.md | 32 +++++++++++++++++++++++++++++++- cmd/envc/convert.go | 37 ++++++++++++++++++++++++++----------- cmd/envc/convert_test.go | 27 +++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 12 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..50632d3 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Go: Launch cmd/ (main)", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/cmd", + "cwd": "${workspaceFolder}" + }, + { + "name": "Go: Debug tests in current file's package", + "type": "go", + "request": "launch", + "mode": "test", + "program": "${fileDirname}", + "cwd": "${workspaceFolder}" + } + ] +} diff --git a/README.md b/README.md index 8517833..73680bd 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,24 @@ b, _ := json.New().Marshal(m) os.WriteFile("out.json", b, 0o644) ``` +### 4. Current process environment → YAML (library) + +```go +ctx := context.Background() +m, err := env.New(env.WithCurrentEnvironment()).Map(ctx) +if err != nil { + return err +} +b, err := yaml.New().Marshal(m) +if err != nil { + return err +} +_, _ = os.Stdout.Write(b) +``` + +Same map as `envc convert --from env --to yaml --input environ` (nested keys come from +splitting `VAR_NAME` on `_`, then **lower+alnum** normalization). + ## CLI: `envc` Install the binary from [**Install**](#install) (`go install …/cmd/envc@latest`). Every @@ -109,7 +127,16 @@ envc version ### `convert` One input → normalize keys → one output. Defaults: **`--input -`**, **`--output -`** -(stdin / stdout). +(stdin / stdout). Use **`--input environ`** with **`--from env`** to read **`os.Environ()`** +(snapshot at run time), then emit **`--to`** (YAML, JSON, etc.) on stdout—no stdin. + +```bash +# Current process environment → nested YAML on stdout (may include secrets; mask before sharing) +envc convert --from env --to yaml --input environ --output - + +# Same snapshot as minified JSON +envc convert --from env --to json --input environ +``` ```bash # Helm-style values file → JSON on the terminal (redirect to a file if you prefer) @@ -208,6 +235,9 @@ set +a ### Stdin, URLs, and EOF ```bash +# Process environment (not stdin): use --input environ with --from env (see `convert` above) +envc convert --from env --to yaml --input environ + # Explicit stdin redirect (reads until EOF) envc convert --from yaml --to json --input - --output - <./service.yaml diff --git a/cmd/envc/convert.go b/cmd/envc/convert.go index 0c2ad6c..8ff7278 100644 --- a/cmd/envc/convert.go +++ b/cmd/envc/convert.go @@ -22,7 +22,7 @@ func RunConvert(args []string, stdin io.Reader, stdout, stderr io.Writer) int { fs.SetOutput(stderr) from := fs.String("from", "", "source format: yaml|json|toml|ini|env") to := fs.String("to", "", "target format: yaml|json|toml|ini|env") - input := fs.String("input", "-", "input path, URL, or - for stdin") + input := fs.String("input", "-", "input path, URL, - for stdin, or \"environ\" for current process env (requires --from env)") output := fs.String("output", "-", "output path or - for stdout") if err := fs.Parse(args); err != nil { return 2 @@ -32,16 +32,31 @@ func RunConvert(args []string, stdin io.Reader, stdout, stderr io.Writer) int { return 2 } ctx := context.Background() - src := openSource(*input, stdin) - b, err := bytesutil.ReadAll(ctx, src) - if err != nil { - _, _ = fmt.Fprintf(stderr, "convert: read: %v\n", err) - return 1 - } - m, err := loadMapFromBytes(ctx, *from, b) - if err != nil { - _, _ = fmt.Fprintf(stderr, "convert: parse: %v\n", err) - return 1 + var m map[string]any + var err error + if *input == "environ" { + if *from != "env" { + _, _ = fmt.Fprintln(stderr, "convert: --input environ only works with --from env") + return 2 + } + m, err = env.New(env.WithCurrentEnvironment()).Map(ctx) + if err != nil { + _, _ = fmt.Fprintf(stderr, "convert: environ: %v\n", err) + return 1 + } + } else { + src := openSource(*input, stdin) + var b []byte + b, err = bytesutil.ReadAll(ctx, src) + if err != nil { + _, _ = fmt.Fprintf(stderr, "convert: read: %v\n", err) + return 1 + } + m, err = loadMapFromBytes(ctx, *from, b) + if err != nil { + _, _ = fmt.Fprintf(stderr, "convert: parse: %v\n", err) + return 1 + } } outb, err := marshalMap(*to, m) if err != nil { diff --git a/cmd/envc/convert_test.go b/cmd/envc/convert_test.go index 5423168..6d4979e 100644 --- a/cmd/envc/convert_test.go +++ b/cmd/envc/convert_test.go @@ -8,6 +8,33 @@ import ( "testing" ) +func TestRunConvert_EnvironToYAML(t *testing.T) { + t.Setenv("ENVC_TEST_PUBLISH", "xyzzy") + var stdout, stderr bytes.Buffer + code := RunConvert([]string{ + "--from", "env", "--to", "yaml", + "--input", "environ", "--output", "-", + }, strings.NewReader(""), &stdout, &stderr) + if code != 0 { + t.Fatalf("stderr: %s", stderr.String()) + } + out := stdout.String() + if !strings.Contains(out, "xyzzy") { + t.Fatalf("expected value in yaml out: %s", out) + } +} + +func TestRuknConvert_EnvironRequiresFromEnv(t *testing.T) { + var stdout, stderr bytes.Buffer + code := RunConvert([]string{ + "--from", "yaml", "--to", "json", + "--input", "environ", "--output", "-", + }, strings.NewReader(""), &stdout, &stderr) + if code != 2 { + t.Fatalf("expected exit 2, got %d stderr=%s", code, stderr.String()) + } +} + func TestRunConvert_TOMLToJSON(t *testing.T) { var stdout, stderr bytes.Buffer stdin := strings.NewReader("[app]\nport = 8080\n")