checkmate

package
v1.2.1 Latest Latest
Warning

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

Go to latest
Published: Apr 14, 2026 License: MIT Imports: 13 Imported by: 0

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

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

func IsTerminal(w io.Writer) bool

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

type CheckResult struct {
	Name     string
	Status   Status
	Error    error
	Duration time.Duration
}

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 DoneMsg

type DoneMsg struct{}

DoneMsg signals completion.

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

func WithTheme(t *Theme) Option

WithTheme sets the theme.

Example:

p := checkmate.New(checkmate.WithTheme(checkmate.MinimalTheme()))

func WithWriter

func WithWriter(w io.Writer) Option

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

func New(opts ...Option) *Printer

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

func (p *Printer) CategoryHeader(title string)

CategoryHeader displays a category header with decorative separators. Example: "─── Code Quality ────────────────────────"

func (*Printer) CheckFailure

func (p *Printer) CheckFailure(title, details, remediation string)

CheckFailure displays a failure with details and remediation guidance. Pass empty strings for details or remediation to omit those sections.

func (*Printer) CheckHeader

func (p *Printer) CheckHeader(message string)

CheckHeader displays a check-in-progress message. Example: "🔍 Checking formatting..."

func (*Printer) CheckInfo

func (p *Printer) CheckInfo(lines ...string)

CheckInfo displays indented informational lines. Example: " Tool: go-licenses"

func (*Printer) CheckLine

func (p *Printer) CheckLine(name string, status Status, duration time.Duration)

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

func (p *Printer) CheckNote(message string)

CheckNote displays an informational note. Example: "Note: This is informational"

func (*Printer) CheckSuccess

func (p *Printer) CheckSuccess(message string)

CheckSuccess displays a success message. Example: "✅ All files properly formatted"

func (*Printer) CheckSummary

func (p *Printer) CheckSummary(status Status, title string, items ...string)

CheckSummary displays a summary box with status and items. status should be StatusSuccess or StatusFailure.

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.

func (ProgressModel) Init

func (m ProgressModel) Init() tea.Cmd

Init initializes the model.

func (ProgressModel) Update

func (m ProgressModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update handles messages.

func (ProgressModel) View

func (m ProgressModel) View() string

View renders the progress display.

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.

func (RunResult) Success

func (r RunResult) Success() bool

Success returns true if all checks passed.

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

func (r *Runner) AddFunc(name string, fn func(ctx context.Context) error) RunnerInterface

AddFunc is a convenience for adding simple checks. Returns the runner for chaining.

Example:

runner.AddFunc("format", checkFormat).AddFunc("lint", checkLint)

func (*Runner) Run

func (r *Runner) Run(ctx context.Context) RunResult

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 Status

type Status string

Status represents the outcome of a check or operation.

const (
	// StatusSuccess indicates a successful check.
	StatusSuccess Status = "success"
	// StatusFailure indicates a failed check.
	StatusFailure Status = "failure"
)

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.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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