mageconsole

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Nov 25, 2025 License: MIT Imports: 20 Imported by: 0

README

mageconsole

A Go library for running shell commands with formatted, colorful console output. Designed for use in build scripts, particularly with Mage.

Note: This library is the engine behind the fo CLI. All features available in the fo command-line tool are also available programmatically via this library. See the main README for CLI usage examples.

Installation

go get github.com/dkoosis/fo/mageconsole

Quick Start

package main

import (
    "github.com/dkoosis/fo/mageconsole"
)

func main() {
    console := mageconsole.DefaultConsole()

    result, err := console.Run("Build", "go", "build", "./...")
    if err != nil {
        // Handle error
    }

    // Check exit code
    if result.ExitCode != 0 {
        // Command failed
    }
}

API Reference

Console

The main type for running commands.

NewConsole(config ConsoleConfig) *Console

Creates a new Console with the specified configuration.

console := mageconsole.NewConsole(mageconsole.ConsoleConfig{
    Stream:         true,        // Stream output live
    ShowOutputMode: "on-fail",   // Show captured output: "always", "on-fail", "never"
    Monochrome:     false,       // Disable colors
})
DefaultConsole() *Console

Creates a Console with sensible defaults.

console := mageconsole.DefaultConsole()
Running Commands
Run(label, command string, args ...string) (*TaskResult, error)

Runs a command with a label and returns detailed results.

result, err := console.Run("Go Test", "go", "test", "-v", "./...")
if err != nil {
    // err is non-nil if command not found or non-zero exit
}
fmt.Printf("Exit code: %d\n", result.ExitCode)
fmt.Printf("Duration: %v\n", result.Duration)
RunSimple(command string, args ...string) error

Simplified interface that returns only an error.

if err := console.RunSimple("go", "build", "./..."); err != nil {
    return err
}
TaskResult

Returned by Run() with command execution details.

type TaskResult struct {
    Label    string        // The label you provided
    Status   string        // "success", "error", "warning"
    ExitCode int           // Command exit code
    Duration time.Duration // Execution time
    Lines    []Line        // Captured output lines (in capture mode)
}
Error Handling

The library exports ErrNonZeroExit for checking exit code errors:

result, err := console.Run("Test", "go", "test", "./...")
if errors.Is(err, mageconsole.ErrNonZeroExit) {
    fmt.Printf("Tests failed with exit code %d\n", result.ExitCode)
}

For command-not-found errors:

result, err := console.Run("Missing", "nonexistent-command")
if errors.Is(err, exec.ErrNotFound) {
    fmt.Printf("Command not found (exit code %d)\n", result.ExitCode)
}

Note: TaskResult is always non-nil, even for infrastructure failures. This allows you to access duration, label, and error details regardless of how the command failed. Use result.ExitCode (127 for command not found) and result.Err for details.

For RunSimple, you can extract the exit code from errors:

err := console.RunSimple("go", "test", "./...")
if errors.Is(err, mageconsole.ErrNonZeroExit) {
    var exitErr mageconsole.ExitCodeError
    if errors.As(err, &exitErr) {
        fmt.Printf("Exit code: %d\n", exitErr.Code)
    }
}

Configuration Options

ConsoleConfig Fields
Field Type Default Description
Stream bool false Stream output live instead of capturing
ShowOutputMode string "on-fail" When to show captured output: "always", "on-fail", "never"
Monochrome bool false Disable ANSI colors
ShowTimer bool true Show execution duration
ThemeName string "unicode_vibrant" Visual theme name
UseBoxes bool true Use box-drawing characters
InlineProgress bool false Use inline progress instead of multi-line
MaxBufferSize int64 10MB Max buffer size for captured output
MaxLineLength int 1MB Max length for a single output line
Out io.Writer os.Stdout Output writer
Err io.Writer os.Stderr Error writer
Debug bool false Enable debug output
Example Configurations

CI Mode (no colors, no timer):

console := mageconsole.NewConsole(mageconsole.ConsoleConfig{
    Monochrome: true,
    ShowTimer:  false,
})

Streaming Mode:

console := mageconsole.NewConsole(mageconsole.ConsoleConfig{
    Stream: true,
})

Custom Writers (for testing):

var stdout, stderr bytes.Buffer
console := mageconsole.NewConsole(mageconsole.ConsoleConfig{
    Out: &stdout,
    Err: &stderr,
})

Usage Patterns

Mage Build Script
//go:build mage

package main

import "github.com/dkoosis/fo/mageconsole"

var console = mageconsole.DefaultConsole()

func Build() error {
    _, err := console.Run("Go Build", "go", "build", "./...")
    return err
}

func Test() error {
    _, err := console.Run("Go Test", "go", "test", "./...")
    return err
}

func QA() error {
    if _, err := console.Run("Format", "go", "fmt", "./..."); err != nil {
        return err
    }
    if _, err := console.Run("Vet", "go", "vet", "./..."); err != nil {
        return err
    }
    return nil
}
Error Aggregation
func RunAll() error {
    var errors []error

    for _, target := range []string{"./cmd/...", "./internal/...", "./pkg/..."} {
        result, err := console.Run("Build "+target, "go", "build", target)
        if err != nil {
            errors = append(errors, fmt.Errorf("%s failed: %w", target, err))
        }
    }

    if len(errors) > 0 {
        return fmt.Errorf("build failed: %v", errors)
    }
    return nil
}
Conditional Output
func VerboseBuild(verbose bool) error {
    mode := "on-fail"
    if verbose {
        mode = "always"
    }

    console := mageconsole.NewConsole(mageconsole.ConsoleConfig{
        ShowOutputMode: mode,
    })

    _, err := console.Run("Build", "go", "build", "-v", "./...")
    return err
}

Themes

Available themes:

  • unicode_vibrant (default) - Colorful with Unicode characters
  • ascii_minimal - Plain ASCII, suitable for limited terminals

Custom themes can be defined in .fo.yaml configuration files.

License

See the main project license.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrNonZeroExit = errors.New("command exited with non-zero code")

ErrNonZeroExit is returned when a command completes but exits with a non-zero code. Use errors.Is(err, ErrNonZeroExit) to check for this condition.

Functions

func HumanizeTestName added in v0.2.0

func HumanizeTestName(testName string) string

HumanizeTestName converts Go test names to human-friendly format. Test<Component>_<Behavior>_When_<Condition> -> "Component: Behavior - When Condition"

Types

type Console

type Console struct {
	// contains filtered or unexported fields
}

func DefaultConsole

func DefaultConsole() *Console

func NewConsole

func NewConsole(cfg ConsoleConfig) *Console

func (*Console) GetBlueFgColor added in v0.2.0

func (c *Console) GetBlueFgColor() string

GetBlueFgColor returns the light blue color code from the theme.

func (*Console) GetBorderChars added in v0.2.0

func (c *Console) GetBorderChars() (topCorner, bottomCorner, headerChar, verticalChar string)

GetBorderChars returns the border characters from the theme.

func (*Console) GetColor added in v0.2.0

func (c *Console) GetColor(colorKey string) string

GetColor returns a color code from the theme by key.

func (*Console) GetErrorColor added in v0.2.0

func (c *Console) GetErrorColor() string

GetErrorColor returns the Error color code from the theme.

func (*Console) GetGreenFgColor added in v0.2.0

func (c *Console) GetGreenFgColor() string

GetGreenFgColor returns the light green color code from the theme.

func (*Console) GetHeaderWidth added in v0.2.0

func (c *Console) GetHeaderWidth() int

GetHeaderWidth returns the header width from the theme.

func (*Console) GetIcon added in v0.2.0

func (c *Console) GetIcon(iconKey string) string

GetIcon returns an icon from the theme by key.

func (*Console) GetMutedColor added in v0.2.0

func (c *Console) GetMutedColor() string

GetMutedColor returns the Muted color code from the theme.

func (*Console) GetSuccessColor added in v0.2.0

func (c *Console) GetSuccessColor() string

GetSuccessColor returns the Success color code from the theme.

func (*Console) GetWarningColor added in v0.2.0

func (c *Console) GetWarningColor() string

GetWarningColor returns the Warning color code from the theme.

func (*Console) PrintH1Header added in v0.2.0

func (c *Console) PrintH1Header(name string)

PrintH1Header prints a major headline (H1) using the console's theme.

func (*Console) PrintSectionFooter added in v0.2.0

func (c *Console) PrintSectionFooter()

PrintSectionFooter closes the section box with a bottom border.

func (*Console) PrintSectionHeader added in v0.2.0

func (c *Console) PrintSectionHeader(name string)

PrintSectionHeader prints a section header and starts a section box.

func (*Console) PrintSectionLine added in v0.2.0

func (c *Console) PrintSectionLine(line string)

PrintSectionLine prints a line of section content with side borders.

func (*Console) ResetColor added in v0.2.0

func (c *Console) ResetColor() string

ResetColor returns the reset color code from the theme.

func (*Console) Run

func (c *Console) Run(label, command string, args ...string) (*TaskResult, error)

Run executes a command and returns the result.

Error semantics:

  • Returns (result, nil) when the command runs successfully (exit code 0)
  • Returns (result, error) when the command runs but exits non-zero; the error wraps the underlying exec.ExitError
  • Returns (result, error) for infrastructure failures (command not found, IO errors, context cancelled)

Note: TaskResult is always non-nil. Even for infrastructure failures, the result contains useful information like duration, label, and any captured internal error messages. Use TaskResult.ExitCode (127 for command not found, 1 for other failures) and TaskResult.Err for failure details.

Use errors.Is(err, exec.ErrNotFound) to check for missing commands.

func (*Console) RunSimple

func (c *Console) RunSimple(command string, args ...string) error

RunSimple executes a command and returns only an error. This is a convenience wrapper around Run for simple use cases where you only need to know success vs failure.

Returns nil on success (exit code 0). Returns ErrNonZeroExit (wrapped with ExitCodeError) if the command exits with non-zero code. Returns other errors for infrastructure failures.

To check for non-zero exit and extract the code:

if errors.Is(err, ErrNonZeroExit) {
    var exitErr mageconsole.ExitCodeError
    if errors.As(err, &exitErr) {
        fmt.Printf("Exit code: %d\n", exitErr.Code)
    }
}

For detailed results including captured output, use Run() instead.

type ConsoleConfig

type ConsoleConfig struct {
	ThemeName      string
	UseBoxes       bool
	UseBoxesSet    bool
	InlineProgress bool
	InlineSet      bool
	Monochrome     bool
	ShowTimer      bool
	ShowTimerSet   bool
	ShowOutputMode string
	Stream         bool
	Pattern        string // Manual pattern selection hint (e.g., "test-table", "sparkline", "leaderboard")
	Debug          bool
	Profile        bool   // Enable performance profiling
	ProfileOutput  string // Profile output destination
	MaxBufferSize  int64
	MaxLineLength  int
	Design         *design.Config
	Out            io.Writer // Output writer, defaults to os.Stdout
	Err            io.Writer // Error writer, defaults to os.Stderr
}

type CoverageThreshold added in v0.2.0

type CoverageThreshold struct {
	Min      float64
	Max      float64
	ColorKey string
}

CoverageThreshold defines a coverage range and its associated color.

type ExitCodeError

type ExitCodeError struct {
	Code int
}

ExitCodeError wraps an exit code for programmatic access. Use errors.As(err, &ExitCodeError{}) to extract the exit code from RunSimple errors.

func (ExitCodeError) Error

func (e ExitCodeError) Error() string

type Line

type Line struct {
	Content   string
	Type      string // "detail", "error", "warning", "success", "info", "progress"
	Timestamp time.Time
}

Line represents a classified line of command output. This is the public-facing type that doesn't leak internal design package types.

type ProfileData

type ProfileData struct {
	Stage          string        `json:"stage"`
	Duration       time.Duration `json:"duration"`
	DurationMs     int64         `json:"duration_ms"`
	PatternMatches int           `json:"pattern_matches,omitempty"`
	PatternSuccess int           `json:"pattern_success,omitempty"`
	BufferSize     int64         `json:"buffer_size,omitempty"`
	LineCount      int           `json:"line_count,omitempty"`
	MemoryAlloc    int64         `json:"memory_alloc,omitempty"`
}

ProfileData tracks performance metrics for each pipeline stage.

type Profiler

type Profiler struct {
	// contains filtered or unexported fields
}

Profiler tracks performance metrics throughout command execution.

func NewProfiler

func NewProfiler(enabled bool, output string) *Profiler

NewProfiler creates a new profiler instance.

func (*Profiler) EndStage

func (p *Profiler) EndStage(name string, start time.Time, metrics map[string]interface{})

EndStage records the duration of a performance stage.

func (*Profiler) StartStage

func (p *Profiler) StartStage(name string) time.Time

StartStage marks the start of a performance stage.

func (*Profiler) Write

func (p *Profiler) Write() error

Write outputs the profile data to the configured destination.

type TaskResult

type TaskResult struct {
	Label    string
	Intent   string
	Status   string
	Duration time.Duration
	ExitCode int
	Lines    []Line
	Err      error
}

func (*TaskResult) ToJSON

func (r *TaskResult) ToJSON() ([]byte, error)

ToJSON converts TaskResult to JSON format for structured output.

type TestPackageResult added in v0.2.0

type TestPackageResult struct {
	Name        string
	Passed      int
	Failed      int
	Skipped     int
	Duration    time.Duration
	Coverage    float64
	FailedTests []string
	AllTests    []TestResult // All tests with their status (when ShowAllTests is enabled)
}

TestPackageResult represents the results for a single test package.

type TestRenderer added in v0.2.0

type TestRenderer struct {
	// contains filtered or unexported fields
}

TestRenderer renders test results using the console's theme.

func NewTestRenderer added in v0.2.0

func NewTestRenderer(console *Console, writer io.Writer) *TestRenderer

NewTestRenderer creates a new test renderer with the given console and writer.

func (*TestRenderer) GetConfig added in v0.2.0

func (r *TestRenderer) GetConfig() TestTableConfig

GetConfig returns the current test table configuration.

func (*TestRenderer) RenderGroupFooter added in v0.2.0

func (r *TestRenderer) RenderGroupFooter()

RenderGroupFooter renders the bottom border of the group box.

func (*TestRenderer) RenderGroupHeader added in v0.2.0

func (r *TestRenderer) RenderGroupHeader(dirName string)

RenderGroupHeader renders the directory group header and starts a box.

func (*TestRenderer) RenderPackageLine added in v0.2.0

func (r *TestRenderer) RenderPackageLine(pkg TestPackageResult)

RenderPackageLine renders a single package test result line.

func (*TestRenderer) RenderTableHeader added in v0.2.0

func (r *TestRenderer) RenderTableHeader()

RenderTableHeader renders the table header with column labels in a complete box.

func (*TestRenderer) SetConfig added in v0.2.0

func (r *TestRenderer) SetConfig(config TestTableConfig)

SetConfig updates the test table configuration.

type TestResult added in v0.2.0

type TestResult struct {
	Name   string
	Status string // "PASS", "FAIL", "SKIP"
}

TestResult represents a single test with its status.

type TestTableConfig added in v0.2.0

type TestTableConfig struct {
	// SparkbarFilled is the character used for filled portions of the sparkbar
	SparkbarFilled string
	// SparkbarEmpty is the character used for empty portions of the sparkbar
	SparkbarEmpty string
	// SparkbarLength is the number of characters in the sparkbar
	SparkbarLength int
	// CoverageThresholds defines the coverage ranges for color coding
	// Format: [[0, 39, "Error"], [40, 69, "Warning"], [70, 100, "Success"]]
	CoverageThresholds []CoverageThreshold
	// ShowPercentage controls whether to show percentage after sparkbar
	ShowPercentage bool
	// UseTreeChars controls whether to use tree characters (├─, └─)
	UseTreeChars bool
	// NoTestIcon is the icon to use for packages with no tests
	NoTestIcon string
	// NoTestColor is the color key for packages with no tests
	NoTestColor string
	// ShowAllTests controls whether to show all tests (including passed) with their status
	ShowAllTests bool
}

TestTableConfig configures how test results are rendered.

Jump to

Keyboard shortcuts

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