Documentation
¶
Overview ¶
Package progress defines the structured run-time event contract for xray's CLI output cluster (cli-ux). A Sink consumes Events emitted at phase boundaries during a run; concrete sinks include a no-op default, a slog wrapper for the non-TTY log fallback, an NDJSON emitter for --output json, and a TTY status grid for --output auto on a terminal.
The contract is the seam imported by sibling cli-ux work: rate-limit and retry visibility (#82), the post-run summary block (#84), and the run-time status display itself (#81). The package depends on the standard library only; concrete sinks may pull in additional helpers.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Event ¶
type Event struct {
Kind EventKind
Repo string
Connector string
Phase string
Done, Total int64
Message string
At time.Time
Fields map[string]any
}
Event is one run-time progress signal. Repo/Connector/Phase identify the (repo, connector) pair the event applies to; global events leave them empty. Done/Total advance on PhaseProgress; sinks that don't know a Total render the partial count alone.
func (Event) IsTransition ¶
IsTransition reports whether an event marks a state transition rather than a mid-walk tick. Sinks that throttle output (LogSink without verbose, the TTY header) use this to keep noise down.
type EventKind ¶
type EventKind string
EventKind names a discrete run-time event. The string values are the wire shape used by --output json and are stable per the schema documented in docs/spec.md ("JSON event schema").
type JSONSink ¶
type JSONSink struct {
// contains filtered or unexported fields
}
JSONSink emits one NDJSON object per Event to a writer. The wire shape matches docs/spec.md "JSON event schema" — additive changes are non-breaking; renames or removals bump output_schema_version.
func NewJSONSink ¶
NewJSONSink wraps a writer (typically stdout). Concurrent Emit calls are serialised so NDJSON lines never interleave.
type LogSink ¶
type LogSink struct {
// contains filtered or unexported fields
}
LogSink emits one slog line per transition event. PhaseProgress events are dropped unless verbose is set; this matches the issue's acceptance criterion "Non-TTY: one line per state transition… Skip PhaseProgress ticks unless --verbose."
func NewLogSink ¶
NewLogSink wires a sink to an existing slog logger. The logger continues to receive xray's existing extraction lines independently; LogSink's output is additive — one line per phase boundary.
type NopSink ¶
type NopSink struct{}
NopSink is the zero-cost default. Used when --output quiet is in effect and when run.Options.Progress is nil.
type RateLimitCounter ¶
type RateLimitCounter struct {
// contains filtered or unexported fields
}
RateLimitCounter accumulates RateLimit events emitted by ratelimit.Transport so the post-run summary can report the cumulative wait count + total seconds. It implements Sink and is intended to be tee'd alongside the run's primary Sink (e.g. via NewTeeSink).
func NewRateLimitCounter ¶
func NewRateLimitCounter() *RateLimitCounter
NewRateLimitCounter returns a zero-value counter ready to consume events.
func (*RateLimitCounter) Emit ¶
func (c *RateLimitCounter) Emit(e Event)
Emit ignores every event except Kind == RateLimit. The wait duration is read from Fields["wait_duration_s"] (int seconds) or Fields["wait_duration_ms"] (int ms) if present; events without either contribute to the wait count but not the duration.
func (*RateLimitCounter) Snapshot ¶
func (c *RateLimitCounter) Snapshot() (waits int, totalSeconds int)
Snapshot returns the cumulative counters. Safe to call concurrently with Emit.
type Sink ¶
type Sink interface {
Emit(Event)
}
Sink consumes events emitted during a run. Implementations must be safe to call from multiple goroutines.
func FromContext ¶
FromContext returns the ambient Sink, or NopSink when none is set. Callers can always Emit unconditionally.
type TTYSink ¶
type TTYSink struct {
// contains filtered or unexported fields
}
TTYSink renders a live (repo × connector) status grid using hand-rolled ANSI cursor-up + clear-from-cursor redraws. Refresh runs at ~5 Hz from a background goroutine started by Start; concurrent Emit calls and the ticker are serialised by mu.
The renderer never depends on terminal capabilities beyond CUU (cursor up) and ED (erase display from cursor); both are universally supported on the terminals xray targets.
func NewTTYSink ¶
NewTTYSink wraps a writer (typically os.Stdout). Start must be called before any Emit for the grid to render incrementally; without Start, Emit still records state and a single render fires on Stop.
func (*TTYSink) Emit ¶
Emit records the event and (if Start has been called) lets the ticker pick it up on the next tick. Sub-tick latency is acceptable at 5 Hz and avoids redraw thrash under high-event-rate phases.
func (*TTYSink) Plan ¶
Plan pre-registers the full (repo × connector) grid so every cell renders as pending from the first frame. Without Plan, cells appear only as their PhaseStart events arrive — fine but less informative.
type TeeSink ¶
type TeeSink struct {
// contains filtered or unexported fields
}
TeeSink fans out a single Emit call to every wrapped sink. Used in the CLI to tee a RateLimitCounter alongside the user-facing TTY or log sink without coupling either to the other.
func NewTeeSink ¶
NewTeeSink returns a sink that forwards every Emit to each of sinks in order. Nil entries are silently skipped.