Skip to content

feat: output formats, JSON errors, exit codes and non-interactive behaviour - #5

Open
RogerCecilpot wants to merge 1 commit into
feat/235-scaffoldfrom
feat/246-output-errors-exit-codes
Open

RogerCecilpot wants to merge 1 commit into
feat/235-scaffoldfrom
feat/246-output-errors-exit-codes

Conversation

@RogerCecilpot

Copy link
Copy Markdown

Closes devlyft/devlyft#246. Stacked on #2; retarget to main once that merges.

Implements sections 6.3, 6.4 and 7 of the design spec.

What

  • Formats (internal/output): --format table|tsv|json|yaml. With no flag: aligned table at a terminal, header-less TSV when piped. JSON/YAML print the API resource shape (YAML via sigs.k8s.io/yaml, so field names match JSON). --jq (gojq, implies JSON; strings print raw like jq -r), --quiet prints IDs. Commands hand the printer a Result{Data, Table, IDs}; a nil Table falls back to YAML for single resources.
  • Streams (internal/iostreams): TTY detection; colour only on a terminal and off with NO_COLOR, --no-color or TERM=dumb.
  • Errors (internal/clierr): one error type with the stable code strings, HTTP-status mapping and retryable. cli.Execute is the only place errors are printed and mapped to exit codes. Human: Error: line, optional Hint:, Request ID:. Under --format json/--jq: one {code,message,http_status,request_id,details,retryable} object on stderr (all keys always present).
  • Exit codes (internal/exitcode): 0, 1, 2, 3, 4, 5, 6, 7, 8, 130. Cobra's own errors (unknown command/flag, wrong number of args) are usage errors (exit 2), detected by whether the root's pre-run hook ran.
  • Non-interactive: prompt.Confirm never prompts without a TTY on stdin and stderr; destructive commands get --yes (cmdutil.YesFlagUsage).
  • Debug logging: --verbosity=debug|info|warning|error|none or DEVLYFT_DEBUG=1, slog to stderr through a redacting handler. internal/redact masks bearer tokens (incl. the WebSocket bearer, <token> subprotocol), JWTs, sensitive JSON fields (*token, secret, password, value, …), query/form params and sensitive headers. The HTTP layer (#236) should log through it.
  • internal/cmdutil.Factory carries IO, printer and logger to command packages.

Tests

Golden files in internal/cli/testdata cover every format, every error code (human and JSON), every exit code including 130, usage errors and confirmation (go test ./internal/cli -update to regenerate). Unit tests for redaction (incl. the logger), the status→code→exit mapping and option validation.

Decisions worth a look

  • Ctrl-C prints Interrupted. and exits 130 with no JSON error object, even under --format json. The spec's code list has no interrupted code and I didn't want to invent one; the exit code is unambiguous. Easy to add if preferred.
  • --quiet can't be combined with --format/--jq; --jq can't be combined with --format yaml|table|tsv.
  • 408/504 map to unavailable (exit 1, retryable) rather than timeout (exit 6), which the spec reserves for --wait/--timeout.
  • YAML keys are sorted (a sigs.k8s.io/yaml property); JSON keeps struct order.

🤖 Generated with Claude Code

…aviour

- --format table|tsv|json|yaml: a table at a terminal, header-less TSV when
  piped; JSON/YAML print the API resource shape. --jq filters JSON with
  gojq (strings print raw), --quiet prints IDs only.
- Colour only on a terminal, off with NO_COLOR, --no-color or TERM=dumb.
- Errors are printed and mapped to exit codes in one place
  (cli.Execute). Under --format json or --jq, stderr carries one
  {code,message,http_status,request_id,details,retryable} object.
- Exit codes 0-8 and 130 per the design spec; Cobra's own errors (unknown
  command, bad flag, wrong args) are usage errors (exit 2).
- prompt.Confirm never prompts without a TTY and asks for --yes instead.
- --verbosity / DEVLYFT_DEBUG=1 logging through a redacting slog handler;
  redact masks bearer tokens, JWTs, sensitive JSON fields, query/form
  parameters and sensitive headers.
- Golden-file tests for every format and exit code.

Refs devlyft/devlyft#246

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant