mageconsole

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 24, 2025 License: MIT Imports: 19 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

This section is empty.

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) 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 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.

Jump to

Keyboard shortcuts

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