output

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 1, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package output centralizes stdout/stderr formatting for testfleet.

All commands produce one of two output modes: human (default) or JSON (when --json is set). JSON output uses a stable envelope shape with a schema_version field so external tooling can pin a format and detect breaking changes.

Hard contract for --json mode:

  • stdout is pure JSON (a single envelope value, optionally trailing newline)
  • all logs go to stderr (use slog)
  • errors are still JSON envelopes (WriteError) but emitted to stderr

Index

Constants

View Source
const SchemaVersion = "v1"

SchemaVersion is the current envelope schema version. Bump whenever the envelope shape changes incompatibly; golden-file tests will fail until regenerated, forcing the bump to be intentional.

Variables

This section is empty.

Functions

func AssertGoldenJSON

func AssertGoldenJSON(t *testing.T, name string, payload any)

AssertGoldenJSON normalizes payload, then either compares it to or writes it to testdata/golden/<name>.json depending on -update.

Normalization rules:

  • keys recognized in placeholderFields have their values replaced
  • URL substrings in any string value become <URL>
  • JSON keys are emitted in sorted order (deterministic diffs)

payload may be []byte (treated as raw JSON), a string (same), or any value (marshalled first). Bytes/string inputs are required to be valid JSON.

func AssertStdoutIsPureJSON

func AssertStdoutIsPureJSON(t *testing.T, stdout []byte) map[string]any

AssertStdoutIsPureJSON parses stdout as a single JSON value and asserts that the envelope-mandatory fields (schema_version, status) are present.

Use this in every CLI test that exercises --json to catch accidental stdout pollution from cobra warnings, slog misconfiguration, or stray prints. The test runner's "got: <bytes>" message echoes the raw stdout to ease debugging.

func WriteError

func WriteError(w io.Writer, code ErrorCode, message string, details any) error

WriteError emits an error envelope to w (typically stderr in --json mode).

The envelope mirrors WriteJSON's shape (schema_version + status) but with status="error" and a populated error field. details is optional and may be nil; when non-nil, it's marshalled inline so callers can attach structured context (machine name, run-id, underlying error string, etc).

func WriteHumanKV

func WriteHumanKV(w io.Writer, pairs ...string) error

WriteHumanKV renders ordered key/value pairs as "key: value" lines.

Pairs is a flat list ["k1", "v1", "k2", "v2", ...]. An odd-length slice is treated as if a trailing empty value were present, to keep callers tolerant of partial data without panicking.

func WriteHumanTable

func WriteHumanTable(w io.Writer, headers []string, rows [][]string) error

WriteHumanTable renders rows as a left-aligned tab-padded table to w.

headers names the column headers; each row must have len(headers) cells. Cells with fewer/more elements are truncated/padded to keep the table rectangular. Empty rows produce a header-only output.

func WriteJSON

func WriteJSON(w io.Writer, payload any) error

WriteJSON marshals payload as a JSON Envelope and writes it to w.

payload may be:

  • an Envelope: written as-is (SchemaVersion auto-filled if empty)
  • any other value: wrapped in an Envelope with Status="ok" and Data=payload

The output is a single line of JSON followed by a newline so that callers can stream multiple envelopes if ever needed.

Types

type Envelope

type Envelope struct {
	SchemaVersion string        `json:"schema_version"`
	Status        string        `json:"status"`
	Data          any           `json:"data,omitempty"`
	Error         *ErrorPayload `json:"error,omitempty"`
}

Envelope is the wrapper around every JSON-mode response.

Status is one of: "ok", "error". Data carries the per-command payload. Error carries a structured ErrorPayload when Status == "error".

type ErrorCode

type ErrorCode string

ErrorCode is the stable machine-readable error identifier emitted in --json mode.

These codes form a public contract: external tooling (skills, CI scripts) branches on them. Renaming or removing a code is a breaking change and requires bumping SchemaVersion.

const (
	ErrMissingEntrypoint   ErrorCode = "missing_entrypoint"
	ErrMachineBusy         ErrorCode = "machine_busy"
	ErrTransferTimeout     ErrorCode = "transfer_timeout"
	ErrRunNotFound         ErrorCode = "run_not_found"
	ErrTailscaleAuthFailed ErrorCode = "tailscale_auth_failed"
	ErrGhNotAuthenticated  ErrorCode = "gh_not_authenticated"
	ErrGhNotInstalled      ErrorCode = "gh_not_installed"
	ErrIssueCreateFailed   ErrorCode = "issue_create_failed"
	ErrInterrupted         ErrorCode = "interrupted"
	ErrSSHFailed           ErrorCode = "ssh_failed"
	ErrMachineNotFound     ErrorCode = "machine_not_found"

	// Batch Part 1 (issue #8).
	ErrInvalidPlan        ErrorCode = "invalid_plan"         // plan.yaml malformed / structurally invalid
	ErrStagingRequired    ErrorCode = "staging_required"     // batch run missing required --staging
	ErrEmptyPlan          ErrorCode = "empty_plan"           // plan resolved to zero entries
	ErrBatchNotFound      ErrorCode = "batch_not_found"      // batch ID not in store
	ErrNoMachineAvailable ErrorCode = "no_machine_available" // selector matched no non-offline machine

	// ErrStoreFailed is a LOCAL state-store / config / IO failure: loading
	// config, resolving the state dir, opening a store, a store read/write
	// (List/Get/Create/Update other than not-found), or copying artifacts into
	// a local evidence dir. It is deliberately NOT a network code — the failure
	// is on this machine, not on the wire. Distinct from tailscale_auth_failed
	// (a real Tailscale API call), ssh_failed (transport), and dispatch_failed.
	ErrStoreFailed ErrorCode = "store_failed"

	// ErrDispatchFailed is an unexpected failure while dispatching a run that did
	// NOT arrive as a typed runner.DispatchError (those carry their own precise
	// code, e.g. ssh_failed / missing_entrypoint / machine_busy). It is the
	// honest fallback for "dispatch broke for a reason we can't classify" and
	// must not masquerade as ssh_failed when the cause was never SSH.
	ErrDispatchFailed ErrorCode = "dispatch_failed"
)

type ErrorPayload

type ErrorPayload struct {
	Code    ErrorCode `json:"code"`
	Message string    `json:"message"`
	Details any       `json:"details,omitempty"`
}

ErrorPayload is the structured error body embedded in the envelope.

type OutputMode

type OutputMode int

OutputMode selects which formatter a command should use.

const (
	ModeHuman OutputMode = iota
	ModeJSON
)

func Mode

func Mode(cmd *cobra.Command) OutputMode

Mode inspects the cobra command for the persistent --json flag. Defaults to ModeHuman if the flag is not registered or not set.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL