Field notes

stave

Stave is a Go-native, make-like command runner forked from mage. Targets are plain exported Go functions in a stavefile, with helpers for dependencies, shell commands, watch mode and git hooks.

Set up

Install and bootstrap7

Install the latest stave into GOBIN
Install with Homebrew, completion included
Build and install from a stave source checkout
Write a starter stavefile.go if no stavefiles exist
Add the module so pkg/st and pkg/sh imports resolve
Run direnv subcommands through the embedded direnv
Reload the .envrc environment without a direnv binary

Everyday

Running targets11

Run the Default target, or list targets when none is set
Run one target, names are case-insensitive
Run several targets one after another
Pass positional arguments to a typed target
Run a target inside a namespace
Run the namespace's Default method
Run a target through an entry in Aliases
Verbose run, shows sh.Run output and dependency lines
Cancel the target context after a timeout
Read stavefiles from another directory
Run targets with DIR as the working directory

Listing and help7

List targets with synopses, [W] marks watch targets
Show a target's full doc comment and usage line
Keep line breaks from doc comments in the list
Print the installed stave version
Show CLI help and every flag
Load target completion into the current fish shell
Print a completion script for bash, zsh, fish or powershell

Workflows

Common recipes11

Make plain stave run the build
Aggregate target that runs checks in parallel
Ordered pipeline for CI
Static Go build
Skip work when the output is up to date
stave.yaml entry to lint before every commit
Next vX.Y.Z tag from Conventional Commits
Next version without the v prefix
Check the changelog has the upcoming version
Link version headings to tag diffs
Constrained, hook-free CI run

Configure

Environment variables13

Same as -v, accepts true, yes or 1
Same as -d
Request dry-run mode, same as --dryrun
Same as --multiline
Go binary used to compile stavefiles
Override the compiled binary cache directory
Hash stavefiles directly instead of relying on GOCACHE
Ignore Default and list targets when none is given
Enable color in a compiled stavefile's own output
Color for target names, e.g. Cyan or HiCyan
Disable colored output entirely
Cap parallelism, also sets GOMAXPROCS for targets
Suppress decorative hook output, automatic in CI

Git hooks and config13

List configured hooks and install status
Show setup instructions for stave.yaml hooks
Write hook scripts to .git/hooks or core.hooksPath
Overwrite existing hooks not installed by stave
Remove every stave-managed hook
Run a hook's targets by hand for debugging
Skip all stave hooks for one command
Trace hook scripts with set -x
Sourced by every hook first, e.g. to fix PATH
Show the effective merged configuration
Create the default user config file
Show config, cache and data directory paths
Project config with go_cmd, verbose, target_color and hooks

Reference

Stavefile structure10

Build tag every stavefile needs on its first line
Stavefiles must be package main
File directive that keeps doc comment line breaks in -l
Subdirectory form, every .go file inside is included without a tag
Target to run when stave is called with no arguments
Extra names that map to targets
Doc comment's first sentence becomes the -l synopsis
Unexported functions are helpers, never targets
Shell helper package
Deps, errors, namespaces and runtime queries

Targets and arguments8

Simplest target signature
Return an error to fail the run
Receive the target context, cancelled on timeout
Typed args parsed from the command line
Context first, then typed arguments
Supported argument types
Pass arguments positionally after the target
Show the usage line, e.g. stave greet <name> <times>

Namespaces and imports7

Declare a namespace type
Method becomes the build:docker target
Runs when only the namespace name is given
Use namespaced targets as dependencies
Namespaced target with arguments as a dependency
Comment above an import exposes its targets at the root
Expose an imported package's targets as ALIAS:target

Dependencies9

Run dependencies in parallel, each once per invocation
Run dependencies one after another
Parallel dependencies sharing a context
Sequential dependencies sharing a context
Wrap a target with arguments for use in Deps
Distinct arguments count as distinct dependencies
Dependencies that restart with a watch rerun
Clear the run-once record so dependencies can run again
Clear the run-once record for specific targets

Shell helpers (pkg/sh)14

Run a command, stdout shown only with -v
Run a command and always print stdout
Add env vars, second arg is the working dir or empty
RunWith that always prints stdout
Capture stdout, trailing newline removed
Capture stdout with extra env and a working dir
Reusable runner, call as goCmd("build", "./...")
Reusable output capturer, call as git("status")
Full I/O control, returns whether the command ran
Run with explicit stdin, stdout and stderr
Piper with extra env and a working dir
Build an *exec.Cmd bound to the active target context
Remove a path recursively, nil if it is missing
Copy a file to dst, preserving permissions

Context and errors10

Fail with a message and a specific exit status
Formatted Fatal
Exit code from an error, 0 for nil, 1 when unknown
False when the command was not found or not executable
Exit code of a failed shell command
Context of the nearest running target
True when -v or STAVEFILE_VERBOSE is set
True when -d or STAVEFILE_DEBUG is set
Go command in use, go unless STAVEFILE_GOCMD is set
Name of the target requested on the command line

File targets and watch10

True if dst is missing or older than any source
Path check with glob sources
Compare newest files in directory trees
True if any source is newer than a time
Newest modification time under paths
Ignore .gitignore patterns up to the repo root
Add a gitignore-style pattern to the ignore list
Rerun this target when matching files change
Run once, then rerun on each change until interrupted
True when the run is in watch mode

Troubleshoot

Compile, cache and debug11

Print debug messages for parsing, compiling and caching
Print DRYRUN lines instead of running sh commands
Force recompilation of the cached stavefile binary
Keep the generated mainfile for inspection
Remove old compiled binaries from the cache dir
Compile with a different go binary
Write a standalone binary of the stavefile
Cross-compile the stavefile binary
Pass linker flags to the compiled binary
Run a compiled stavefile with no Go toolchain needed
Compiled binaries accept -v, -d, -i and -t