Documentation
¶
Overview ¶
Package checkmate provides beautiful terminal output for developer tools.
Checkmate is designed for CLI tools that run checks, validations, or build processes. It provides consistent, visually appealing output with automatic TTY detection for graceful degradation in CI environments.
Basic Usage ¶
p := checkmate.New()
p.CategoryHeader("Code Quality")
p.CheckHeader("Checking formatting")
p.CheckSuccess("All files properly formatted")
Themes ¶
Checkmate includes two built-in themes:
- DefaultTheme(): Colorful output with emojis for interactive terminals
- MinimalTheme(): Plain ASCII for CI/CD pipelines and piped output
TTY detection is automatic - when output is piped or redirected, checkmate automatically switches to MinimalTheme unless ForceColors is set.
// Force a specific theme p := checkmate.New(checkmate.WithTheme(checkmate.MinimalTheme()))
Writing to Different Outputs ¶
// Write to stderr p := checkmate.New(checkmate.WithStderr()) // Write to a buffer (for testing) var buf bytes.Buffer p := checkmate.New(checkmate.WithWriter(&buf))
Testing with Mock ¶
Use MockPrinter for testing code that uses checkmate:
func TestMyChecker(t *testing.T) {
mock := checkmate.NewMockPrinter()
myChecker := NewMyChecker(mock) // Accepts PrinterInterface
myChecker.Run()
assert.True(t, mock.HasCall("CheckSuccess"))
assert.Equal(t, 1, mock.CallCount("CheckHeader"))
}
Thread Safety ¶
All Printer methods are thread-safe and can be called concurrently. This allows multiple goroutines to report progress simultaneously.
Output Examples ¶
CategoryHeader("Code Quality"):
─── Code Quality ────────────────────────
CheckHeader("Checking formatting"):
🔍 Checking formatting...
CheckSuccess("All files formatted"):
✅ All files formatted
CheckFailure with details:
❌ Format check failed Details: main.go:10: line too long How to fix: • Run: task format
CheckSummary:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ✅ All checks passed (5/5) • Formatting • Linting • Tests ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Check Runner ¶
Use Runner to orchestrate multiple checks with automatic output handling:
p := checkmate.New()
result := checkmate.NewRunner(p, checkmate.WithCategory("Code Quality")).
AddFunc("format", checkFormat).WithRemediation("Run: task format").
AddFunc("lint", checkLint).WithRemediation("Run: task lint").
Run(context.Background())
if !result.Success() {
os.Exit(1)
}
The runner automatically:
- Displays category headers
- Shows check progress with CheckHeader
- Reports success/failure for each check
- Generates a summary at the end
- Recovers from panics (converts to failed checks)
- Respects context cancellation
Runner Options ¶
// Stop on first failure
runner := checkmate.NewRunner(p, checkmate.WithFailFast())
// Set category header
runner := checkmate.NewRunner(p, checkmate.WithCategory("Tests"))
// Run checks in parallel (all at once)
runner := checkmate.NewRunner(p, checkmate.WithParallel())
// Run checks in parallel with limited concurrency
runner := checkmate.NewRunner(p, checkmate.WithWorkers(3))
Parallel Execution ¶
Use WithParallel() to run checks concurrently for faster execution:
result := checkmate.NewRunner(p, checkmate.WithParallel()).
AddFunc("format", checkFormat).
AddFunc("lint", checkLint). // Runs concurrently with format
AddFunc("test", checkTest). // Runs concurrently with both
Run(ctx)
Parallel execution maintains result order - even though checks may complete in any order, the results and output are reported in the original order.
Use WithWorkers(n) to limit concurrency:
// Run at most 2 checks at a time runner := checkmate.NewRunner(p, checkmate.WithWorkers(2))
Fail-fast works with parallel execution - when enabled, remaining checks are cancelled after the first failure:
Fluent API ¶
The runner supports a fluent API for easy check definition:
result := checkmate.NewRunner(p).
AddFunc("check1", func(ctx context.Context) error {
return nil // success
}).WithRemediation("Fix instruction").
Add(checkmate.Check{
Name: "check2",
Fn: check2Func,
Remediation: "How to fix",
Details: "Additional context shown on failure",
}).
Run(ctx)
Testing Runners ¶
Use MockPrinter to test code that uses Runner:
func TestChecks(t *testing.T) {
mock := checkmate.NewMockPrinter()
result := checkmate.NewRunner(mock).
AddFunc("test", func(ctx context.Context) error { return nil }).
Run(context.Background())
assert.True(t, result.Success())
assert.True(t, mock.HasCall("CheckSuccess"))
}
Index ¶
- func IsStderrTerminal() bool
- func IsStdoutTerminal() bool
- func IsTerminal(w io.Writer) bool
- type Check
- type CheckProgress
- type CheckResult
- type CheckStatus
- type CheckUpdateMsg
- type CoverageMsg
- type DoneMsg
- type MockCall
- type MockPrinter
- func (m *MockPrinter) CallCount(method string) int
- func (m *MockPrinter) CategoryHeader(title string)
- func (m *MockPrinter) CheckFailure(title, details, remediation string)
- func (m *MockPrinter) CheckHeader(message string)
- func (m *MockPrinter) CheckInfo(lines ...string)
- func (m *MockPrinter) CheckLine(name string, status Status, duration time.Duration)
- func (m *MockPrinter) CheckNote(message string)
- func (m *MockPrinter) CheckSuccess(message string)
- func (m *MockPrinter) CheckSummary(status Status, title string, items ...string)
- func (m *MockPrinter) GetCalls(method string) [][]interface{}
- func (m *MockPrinter) HasCall(method string) bool
- func (m *MockPrinter) Output() string
- func (m *MockPrinter) Reset()
- type MockRunner
- func (m *MockRunner) Add(check Check) RunnerInterface
- func (m *MockRunner) AddFunc(name string, fn func(ctx context.Context) error) RunnerInterface
- func (m *MockRunner) CheckCount() int
- func (m *MockRunner) GetCheck(name string) *Check
- func (m *MockRunner) HasCheck(name string) bool
- func (m *MockRunner) Reset()
- func (m *MockRunner) Run(ctx context.Context) RunResult
- func (m *MockRunner) RunCalls() int
- func (m *MockRunner) SetResult(result RunResult)
- func (m *MockRunner) WithDetails(text string) RunnerInterface
- func (m *MockRunner) WithRemediation(text string) RunnerInterface
- type Option
- type Printer
- func (p *Printer) CategoryHeader(title string)
- func (p *Printer) CheckFailure(title, details, remediation string)
- func (p *Printer) CheckHeader(message string)
- func (p *Printer) CheckInfo(lines ...string)
- func (p *Printer) CheckLine(name string, status Status, duration time.Duration)
- func (p *Printer) CheckNote(message string)
- func (p *Printer) CheckSuccess(message string)
- func (p *Printer) CheckSummary(status Status, title string, items ...string)
- type PrinterInterface
- type ProgressModel
- type ProgressModelOption
- type RunResult
- type Runner
- func (r *Runner) Add(check Check) RunnerInterface
- func (r *Runner) AddFunc(name string, fn func(ctx context.Context) error) RunnerInterface
- func (r *Runner) Run(ctx context.Context) RunResult
- func (r *Runner) WithDetails(text string) RunnerInterface
- func (r *Runner) WithRemediation(text string) RunnerInterface
- type RunnerInterface
- type RunnerOption
- type Status
- type Theme
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsStderrTerminal ¶
func IsStderrTerminal() bool
IsStderrTerminal returns true if stderr is an interactive terminal.
func IsStdoutTerminal ¶
func IsStdoutTerminal() bool
IsStdoutTerminal returns true if stdout is an interactive terminal.
func IsTerminal ¶
IsTerminal determines if the given writer is an interactive terminal. This is used to decide whether to use colors and emojis (terminal) or plain ASCII output (non-terminal).
Returns false for: - Piped output - Redirected output - Non-file writers (like bytes.Buffer)
Types ¶
type Check ¶
type Check struct {
// Name identifies this check (e.g., "format", "lint")
Name string
// Fn runs the check. Return nil for success, error for failure.
Fn func(ctx context.Context) error
// Remediation is shown on failure (e.g., "Run: task format")
Remediation string
// Details is shown on failure (optional additional context)
Details string
}
Check represents a single check to run.
type CheckProgress ¶
type CheckProgress struct {
Name string
Status CheckStatus
Progress float64 // 0.0 to 1.0
Duration time.Duration
Error error
Remediation string // How to fix the error
}
CheckProgress represents a check with progress tracking.
type CheckResult ¶
CheckResult represents the outcome of a single check.
type CheckStatus ¶
type CheckStatus int
CheckStatus represents the status of a check.
const ( CheckPending CheckStatus = iota CheckRunning CheckPassed CheckFailed )
type CheckUpdateMsg ¶
type CheckUpdateMsg struct {
Index int
Status CheckStatus
Progress float64
Duration time.Duration
Error error
Remediation string // How to fix the error (shown on failure)
}
CheckUpdateMsg updates a check's status.
type CoverageMsg ¶
type CoverageMsg struct {
Coverage float64 // 0.0 to 100.0
}
CoverageMsg updates the code coverage percentage.
type MockCall ¶
type MockCall struct {
Method string
Args []interface{}
}
MockCall represents a single method call to the printer.
type MockPrinter ¶
type MockPrinter struct {
// Buffer captures any output written (currently unused but available for future use)
Buffer bytes.Buffer
// Calls records all method invocations with their arguments
Calls []MockCall
// contains filtered or unexported fields
}
MockPrinter records all output for testing. It captures method calls and arguments for verification.
Example:
mock := checkmate.NewMockPrinter()
myChecker := NewMyChecker(mock)
myChecker.Run()
assert.True(t, mock.HasCall("CheckSuccess"))
assert.Equal(t, 1, mock.CallCount("CheckHeader"))
func NewMockPrinter ¶
func NewMockPrinter() *MockPrinter
NewMockPrinter creates a new MockPrinter for testing.
func (*MockPrinter) CallCount ¶
func (m *MockPrinter) CallCount(method string) int
CallCount returns the number of times a method was called.
func (*MockPrinter) CategoryHeader ¶
func (m *MockPrinter) CategoryHeader(title string)
CategoryHeader records the call.
func (*MockPrinter) CheckFailure ¶
func (m *MockPrinter) CheckFailure(title, details, remediation string)
CheckFailure records the call.
func (*MockPrinter) CheckHeader ¶
func (m *MockPrinter) CheckHeader(message string)
CheckHeader records the call.
func (*MockPrinter) CheckInfo ¶
func (m *MockPrinter) CheckInfo(lines ...string)
CheckInfo records the call.
func (*MockPrinter) CheckLine ¶
func (m *MockPrinter) CheckLine(name string, status Status, duration time.Duration)
CheckLine records the call.
func (*MockPrinter) CheckNote ¶
func (m *MockPrinter) CheckNote(message string)
CheckNote records the call.
func (*MockPrinter) CheckSuccess ¶
func (m *MockPrinter) CheckSuccess(message string)
CheckSuccess records the call.
func (*MockPrinter) CheckSummary ¶
func (m *MockPrinter) CheckSummary(status Status, title string, items ...string)
CheckSummary records the call.
func (*MockPrinter) GetCalls ¶
func (m *MockPrinter) GetCalls(method string) [][]interface{}
GetCalls returns all argument lists for calls to a specific method. Each element is a slice of arguments passed to that method call.
Example:
calls := mock.GetCalls("CheckFailure")
assert.Equal(t, "title", calls[0][0])
assert.Equal(t, "details", calls[0][1])
func (*MockPrinter) HasCall ¶
func (m *MockPrinter) HasCall(method string) bool
HasCall checks if a method was called at least once.
func (*MockPrinter) Output ¶
func (m *MockPrinter) Output() string
Output returns all captured output as a string.
func (*MockPrinter) Reset ¶
func (m *MockPrinter) Reset()
Reset clears all recorded calls and output.
type MockRunner ¶
type MockRunner struct {
// contains filtered or unexported fields
}
MockRunner records all check registrations and run calls for testing. It allows you to verify that checks were registered correctly without actually running them.
Example:
mock := checkmate.NewMockRunner()
registerChecks(mock)
assert.Equal(t, 3, mock.CheckCount())
assert.True(t, mock.HasCheck("format"))
func NewMockRunner ¶
func NewMockRunner() *MockRunner
NewMockRunner creates a new MockRunner for testing.
func (*MockRunner) Add ¶
func (m *MockRunner) Add(check Check) RunnerInterface
Add records the check. Returns the mock for chaining.
func (*MockRunner) AddFunc ¶
func (m *MockRunner) AddFunc(name string, fn func(ctx context.Context) error) RunnerInterface
AddFunc records the check. Returns the mock for chaining.
func (*MockRunner) CheckCount ¶
func (m *MockRunner) CheckCount() int
CheckCount returns the number of checks registered.
func (*MockRunner) GetCheck ¶
func (m *MockRunner) GetCheck(name string) *Check
GetCheck returns the check with the given name, or nil if not found.
func (*MockRunner) HasCheck ¶
func (m *MockRunner) HasCheck(name string) bool
HasCheck checks if a check with the given name was registered.
func (*MockRunner) Reset ¶
func (m *MockRunner) Reset()
Reset clears all recorded checks and run calls.
func (*MockRunner) Run ¶
func (m *MockRunner) Run(ctx context.Context) RunResult
Run records the call and returns a configurable result.
func (*MockRunner) RunCalls ¶
func (m *MockRunner) RunCalls() int
RunCalls returns the number of times Run was called.
func (*MockRunner) SetResult ¶
func (m *MockRunner) SetResult(result RunResult)
SetResult sets the result that Run() will return.
func (*MockRunner) WithDetails ¶
func (m *MockRunner) WithDetails(text string) RunnerInterface
WithDetails sets details for the last check. Returns the mock for chaining.
func (*MockRunner) WithRemediation ¶
func (m *MockRunner) WithRemediation(text string) RunnerInterface
WithRemediation sets remediation for the last check. Returns the mock for chaining.
type Option ¶
type Option func(*Printer)
Option configures a Printer.
func WithStderr ¶
func WithStderr() Option
WithStderr is a convenience option to write to stderr.
Example:
p := checkmate.New(checkmate.WithStderr())
func WithTheme ¶
WithTheme sets the theme.
Example:
p := checkmate.New(checkmate.WithTheme(checkmate.MinimalTheme()))
func WithWriter ¶
WithWriter sets the output writer.
Example:
var buf bytes.Buffer p := checkmate.New(checkmate.WithWriter(&buf))
type Printer ¶
type Printer struct {
// contains filtered or unexported fields
}
Printer renders check output to a writer. All methods are thread-safe for concurrent use.
func New ¶
New creates a new Printer with the given options. Default: writes to stdout with DefaultTheme, auto-detecting TTY.
Example:
p := checkmate.New()
p.CheckHeader("Running tests")
p.CheckSuccess("All tests passed")
func (*Printer) CategoryHeader ¶
CategoryHeader displays a category header with decorative separators. Example: "─── Code Quality ────────────────────────"
func (*Printer) CheckFailure ¶
CheckFailure displays a failure with details and remediation guidance. Pass empty strings for details or remediation to omit those sections.
func (*Printer) CheckHeader ¶
CheckHeader displays a check-in-progress message. Example: "🔍 Checking formatting..."
func (*Printer) CheckInfo ¶
CheckInfo displays indented informational lines. Example: " Tool: go-licenses"
func (*Printer) CheckLine ¶
CheckLine displays a single-line check result with duration. Used in non-TTY mode to mimic TUI output structure. Example: "format .......................... [OK] 1.451s"
func (*Printer) CheckNote ¶
CheckNote displays an informational note. Example: "Note: This is informational"
func (*Printer) CheckSuccess ¶
CheckSuccess displays a success message. Example: "✅ All files properly formatted"
type PrinterInterface ¶
type PrinterInterface interface {
// CategoryHeader displays a category header with decorative separators.
// Example output: "─── Code Quality ────────────────────────"
CategoryHeader(title string)
// CheckHeader displays a check-in-progress message.
// Example output: "🔍 Checking formatting..."
CheckHeader(message string)
// CheckSuccess displays a success message.
// Example output: "✅ All files properly formatted"
CheckSuccess(message string)
// CheckFailure displays a failure with details and remediation guidance.
// Example output:
// "❌ Format check failed"
// "Details:"
// " <details>"
// "How to fix:"
// " • <remediation>"
CheckFailure(title, details, remediation string)
// CheckSummary displays a summary box with status and items.
// Example output:
// "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
// "✅ All checks passed"
// ""
// "• Item 1"
// "• Item 2"
// "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
CheckSummary(status Status, title string, items ...string)
// CheckInfo displays indented informational lines.
// Example output: " Tool: go-licenses"
CheckInfo(lines ...string)
// CheckNote displays an informational note.
// Example output: "Note: This is informational"
CheckNote(message string)
// CheckLine displays a single-line check result with duration.
// Used in non-TTY mode to mimic TUI output structure.
// Example output: "format .......................... [OK] 1.451s"
CheckLine(name string, status Status, duration time.Duration)
}
PrinterInterface defines the contract for check output. Use this interface for dependency injection in your code, allowing easy substitution of MockPrinter in tests.
type ProgressModel ¶
type ProgressModel struct {
// contains filtered or unexported fields
}
ProgressModel is the Bubble Tea model for progress display.
func NewProgressModel ¶
func NewProgressModel(title string, checkNames []string, opts ...ProgressModelOption) ProgressModel
NewProgressModel creates a new progress display model.
type ProgressModelOption ¶
type ProgressModelOption func(*ProgressModel)
ProgressModelOption configures a ProgressModel.
func WithSkipSummary ¶
func WithSkipSummary() ProgressModelOption
WithSkipSummary disables the summary box at the end. Use this when showing multiple categories and you want one final summary.
type RunResult ¶
type RunResult struct {
Passed int
Failed int
Total int
Checks []CheckResult
Duration time.Duration
}
RunResult represents the outcome of running all checks.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner orchestrates running multiple checks with beautiful output. All methods are thread-safe for concurrent use.
func NewRunner ¶
func NewRunner(printer PrinterInterface, opts ...RunnerOption) *Runner
NewRunner creates a runner that outputs to the given printer.
Example:
runner := checkmate.NewRunner(printer, checkmate.WithCategory("Code Quality"))
result := runner.AddFunc("lint", lintFn).Run(ctx)
func (*Runner) Add ¶
func (r *Runner) Add(check Check) RunnerInterface
Add adds a check to the runner. Returns the runner for chaining.
Example:
runner.Add(checkmate.Check{
Name: "security",
Fn: checkSecurity,
Remediation: "Run: task check:vuln",
})
func (*Runner) AddFunc ¶
AddFunc is a convenience for adding simple checks. Returns the runner for chaining.
Example:
runner.AddFunc("format", checkFormat).AddFunc("lint", checkLint)
func (*Runner) Run ¶
Run executes all checks and returns results. Automatically prints category header, check headers, success/failure, and summary. Respects context cancellation.
When WithParallel() is enabled, checks run concurrently. Use WithWorkers(n) to limit concurrency.
Example:
result := runner.Run(ctx)
if !result.Success() {
os.Exit(1)
}
func (*Runner) WithDetails ¶
func (r *Runner) WithDetails(text string) RunnerInterface
WithDetails sets details text for the last added check. Returns the runner for chaining.
Example:
runner.AddFunc("security", checkSecurity).WithDetails("Uses govulncheck")
func (*Runner) WithRemediation ¶
func (r *Runner) WithRemediation(text string) RunnerInterface
WithRemediation sets remediation text for the last added check. Returns the runner for chaining.
Example:
runner.AddFunc("format", checkFormat).WithRemediation("Run: task format")
type RunnerInterface ¶
type RunnerInterface interface {
// Add adds a check to the runner. Returns the runner for chaining.
Add(check Check) RunnerInterface
// AddFunc is a convenience for adding simple checks.
AddFunc(name string, fn func(ctx context.Context) error) RunnerInterface
// WithRemediation sets remediation text for the last added check.
WithRemediation(text string) RunnerInterface
// WithDetails sets details text for the last added check.
WithDetails(text string) RunnerInterface
// Run executes all checks and returns results.
Run(ctx context.Context) RunResult
}
RunnerInterface defines the contract for running checks. Use this interface for dependency injection in your code, allowing easy substitution of MockRunner in tests.
type RunnerOption ¶
type RunnerOption func(*Runner)
RunnerOption configures a Runner.
func WithCategory ¶
func WithCategory(name string) RunnerOption
WithCategory sets category header displayed before checks. Example: WithCategory("Code Quality")
func WithFailFast ¶
func WithFailFast() RunnerOption
WithFailFast stops execution on first failure. Default behavior is to run all checks regardless of failures.
func WithParallel ¶
func WithParallel() RunnerOption
WithParallel enables parallel check execution. All checks run concurrently by default. Use WithWorkers to limit concurrency.
Example:
runner := checkmate.NewRunner(printer, checkmate.WithParallel())
func WithShowTiming ¶
func WithShowTiming() RunnerOption
WithShowTiming displays duration for each check in the output. Shows timing like "format passed (1.234s)" instead of just "format passed".
Example:
runner := checkmate.NewRunner(printer, checkmate.WithShowTiming())
func WithWorkers ¶
func WithWorkers(n int) RunnerOption
WithWorkers sets the number of concurrent workers for parallel execution. Implies WithParallel(). A value of 0 means unlimited (all checks run concurrently).
Example:
// Run at most 3 checks concurrently runner := checkmate.NewRunner(printer, checkmate.WithWorkers(3))
type Theme ¶
type Theme struct {
// Icons (lipgloss style)
IconPending string // For CheckHeader (default: ○)
IconSuccess string // For CheckSuccess (default: ✓)
IconFailure string // For CheckFailure (default: ✗)
IconBullet string // For list items (default: •)
IconWarning string // For warnings (default: !)
// Tree connectors
TreeBranch string // Middle item connector (default: ├──)
TreeLast string // Last item connector (default: └──)
TreeLine string // Vertical line (default: │)
// Separators
CategoryChar string // Character for category header line (default: ─)
SummaryChar string // Character for summary box (default: ─)
// Widths
CategoryWidth int // Width of category header (default: 50)
SummaryWidth int // Width of summary separator (default: 50)
// Styles (lipgloss)
SuccessStyle lipgloss.Style
FailureStyle lipgloss.Style
WarningStyle lipgloss.Style
CategoryStyle lipgloss.Style
NoteStyle lipgloss.Style
InfoStyle lipgloss.Style
PendingStyle lipgloss.Style // For in-progress checks
TreeStyle lipgloss.Style // For tree connectors
// Behavior
ForceColors bool // Force colors even in non-TTY (useful for testing)
}
Theme defines the visual appearance of check output. Create custom themes by copying DefaultTheme() and modifying values.
func CITheme ¶
func CITheme() *Theme
CITheme is an alias for MinimalTheme, optimized for CI/CD pipelines.
func DefaultTheme ¶
func DefaultTheme() *Theme
DefaultTheme returns the default lipgloss-style theme. Uses clean Unicode icons and tree connectors.
func MinimalTheme ¶
func MinimalTheme() *Theme
MinimalTheme returns a theme without colors or emojis. Suitable for CI environments, piped output, or accessibility needs.