HACKING.md
This document provides a comprehensive guide for developing cheat, including setup, architecture overview, and code patterns.
The following are required and must be available on your PATH:
gitgo (>= 1.19 is recommended)makeOptional dependencies:
dockerpandoc (necessary to generate a man page)Run make setup to install scc and revive, which are used by various make targets.
cheat source codemake test to run unit-testsmake build. A cheat executable will be written to the dist directorydist/cheat <command>make install to install cheat to your PATHmake build-release to build cross-platform binaries in distmake clean to clean the dist directory when desiredYou may run make help to see a list of available make commands.
Run unit tests with:
make test
Integration tests that require network access are separated using build tags. Run them with:
make test-integration
To run all tests (unit and integration):
make test-all
Generate a coverage report with:
make coverage # HTML report
make coverage-text # Terminal output
The cheat application follows a clean architecture with well-separated concerns:
cmd/cheat/: Command layer (cobra-based CLI, flag registration, command routing, shell completions)internal/config: Configuration management (YAML loading, validation, paths)internal/cheatpath: Cheatsheet path management (collections, filtering)internal/sheet: Individual cheatsheet handling (parsing, search, highlighting)internal/sheets: Collection operations (loading, consolidation, filtering)internal/display: Output formatting (pager integration, colorization)internal/repo: Git repository management for community sheetsinternal/config)The main configuration structure:
type Config struct {
Colorize bool `yaml:"colorize"`
Editor string `yaml:"editor"`
Cheatpaths []cp.Path `yaml:"cheatpaths"`
Style string `yaml:"style"`
Formatter string `yaml:"formatter"`
Pager string `yaml:"pager"`
Path string
}
Key functions:
New(confPath, resolve) - Load config from fileValidate() - Validate configuration valuesEditor() - Get editor from environment or defaults (package-level function)Pager() - Get pager from environment or defaults (package-level function)internal/cheatpath)Represents a directory containing cheatsheets:
type Path struct {
Name string // Friendly name (e.g., "personal")
Path string // Filesystem path
Tags []string // Tags applied to all sheets in this path
ReadOnly bool // Whether sheets can be modified
}
internal/sheet)Represents an individual cheatsheet:
type Sheet struct {
Title string // Sheet name (from filename)
CheatPath string // Name of the cheatpath this sheet belongs to
Path string // Full filesystem path
Text string // Content (without frontmatter)
Tags []string // Combined tags (from frontmatter + cheatpath)
Syntax string // Syntax for highlighting
ReadOnly bool // Whether sheet can be edited
}
Key methods:
New(title, cheatpath, path, tags, readOnly) - Load from fileSearch(reg) - Search content with a compiled regexpColorize(conf) - Apply syntax highlighting (modifies sheet in place)Tagged(needle) - Check if sheet has the given tag// Load sheet
s, err := sheet.New("tar", "personal", "/path/to/tar", []string{"personal"}, false)
if err != nil {
log.Fatal(err)
}
// Apply syntax highlighting (modifies sheet in place)
s.Colorize(conf)
// Display with pager
display.Write(s.Text, conf)
// Load all sheets from cheatpaths (returns a slice of maps, one per cheatpath)
allSheets, err := sheets.Load(conf.Cheatpaths)
if err != nil {
log.Fatal(err)
}
// Consolidate to handle duplicates (later cheatpaths take precedence)
consolidated := sheets.Consolidate(allSheets)
// Filter by tag (operates on the slice of maps)
filtered := sheets.Filter(allSheets, []string{"networking"})
// Sort alphabetically (returns a sorted slice)
sorted := sheets.Sort(consolidated)
Cheatsheets are plain text files that may begin with YAML frontmatter:
---
syntax: bash
tags: [networking, linux, ssh]
---
# Connect to remote server
ssh user@hostname
# Copy files over SSH
scp local_file user@hostname:/remote/path
Run tests with:
make test # Run all tests
make coverage # Generate coverage report
go test ./... # Go test directly
Test files follow Go conventions:
*_test.go files in same packagemocks packageThe codebase follows consistent error handling patterns:
fmt.ErrorfExample:
s, err := sheet.New(title, cheatpath, path, tags, false)
if err != nil {
return fmt.Errorf("failed to load sheet: %w", err)
}
It may be useful to test your changes within a pristine environment. An Alpine-based docker container has been provided for that purpose.
Build the docker container:
make docker-setup
Shell into the container:
make docker-sh
The cheat source code will be mounted at /app within the container.
To destroy the container:
make distclean