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")