CLI reference¶
Global options¶
Global options bind to fm itself and go before the first task name
(fm --json test, not fm test --json):
| option | effect |
|---|---|
-h, --help |
help for fm, or the named group/task |
-V, --version |
print the version and exit |
-l, --list |
list tasks (flat) |
--tree |
list tasks grouped by command group |
--where TASK |
print the task's source file:line |
-n, --dry-run |
print the parsed plan without running |
-k, --keep-going |
run every branch even if one fails |
--fail-fast |
stop at the first failure |
-s, --sequential |
run one at a time (default: parallel) |
-j, --jobs N |
max parallel tasks (default: cores - 1) |
-y, --yes |
assume yes to every confirm() gate |
--no-input |
never prompt; error if input is required |
-q, --quiet |
suppress the per-task summary |
-v, --verbose |
replay captured output even on success |
--color WHEN |
when to colour: always|never|auto (default) |
--no-color |
disable ANSI colour (same as --color=never) |
--no-progress |
no progress bar, eta, or timing capture |
--json |
stdout is one JSON document (captures output) |
--timings |
show per-task durations |
-C, --directory PATH |
run as if launched from PATH |
-f, --tasks-file PATH |
only this tasks file, no tasks cascade |
--config PATH |
only this config file, no config cascade |
--install-completion [SHELL] |
install shell completion |
--setup-completion [SHELL] |
print completion for eval |
--uninstall-completion [SHELL] |
remove the completion hook |
--help is the one global allowed anywhere before --: fm deploy --help
is a read-only help request, never an execution. fm --help documents the
runner and its globals, fm --help docs a group, fm --help deploy a task.
Because it wins wherever it lands, help is also the one parameter name a task
can't use — it would map to --help, which is always intercepted (see
Typed signatures).
Decorator surface¶
from footman import task, group, finalize
@task # bare
def build(): ...
@task(name="ci-build") # override the command name
def build(): ...
@task(pre=[fmt], post=[notify]) # dependencies (run before / after)
def check(): ...
@task(keep_going=True) # surface every failure in the subtree, don't stop at the first
def check(): ...
@task(atomic=True) # never kill this task's subprocesses mid-flight on a sibling failure
def format(): ...
@task(confirm="Deploy to prod?") # a yes/no gate before the task and its prereqs run
def deploy(): ...
@task(interactive=True) # owns the real terminal; the run goes fully sequential
def scaffold(): ...
@task(progress=False) # duration has no pattern: never timed, only pulses
def repl(): ...
@task(infinite=True) # runs until Ctrl-C: hinted at start, never timed
def serve(): ...
fmt.opts(atomic=True) # override a task's policy for one use (pre=/post=/body call)
release = group("release", help="Cut a release")
@release.task
def wheel(): ...
@finalize # edit the merged task tree at discovery
def gate(tasks): # (see Monorepos & config)
for t in tasks:
t.add_pre(tasks["audit"])
Runtime helpers¶
| Import | Purpose |
|---|---|
run(cmd, ...) |
run a command or callable in the task context |
parallel(*calls) |
fan tasks/thunks out concurrently |
passthrough() |
arguments after -- on the command line |
Context, use_context |
the task context; install one from your own code |
Many[T], nosplit |
one-or-many; opt a collection out of comma-splitting |
forward, Forward[T] |
thread a value to the tasks/groups this task dispatches — see Chaining & parallelism |
ask("prompt") |
prompt for a missing value (CLI > env > default > prompt); CI-safe |
prompt(), confirm(), select() |
mid-task questions — only inside an interactive=True task |
.opts(**policy) |
per-use policy override (keep_going, atomic, …) on a task or group |
Annotated[T, suggest(fn)] |
dynamic completion for a parameter |
exists, isfile, isdir |
require a Path value to exist on disk |
between(lo, hi) |
inclusive numeric bounds (a bare range works too) |
env("VAR") |
fall back to an environment variable (CLI > env > default) |
check(fn) |
custom post-coercion validator (ValueError rejects) |
doc("…") |
one-line parameter help — shown in --help, completion, the catalog |
footman.tools |
typed wrappers for ruff, basedpyright, pytest, uv, … |
footman.testing |
Runner/Result + recording() — see Testing |
include, plugin |
adopt tasks from modules/packages — see Composing |
inherited() |
the task this one shadows in the cascade — call it to extend it |
progress(done, total), track(iterable) |
report a task's own progress; the bar fills from it |
fetch(url, …) |
download into footman's cache — revalidating, verifying, recorded as a step |
@requires_dep/_tool/_env, @requires |
disable-but-list a task that can't run here |
Configuration¶
Settings have their own page — the precedence ladder (user-level file,
the project cascade, --config, environment, flags), the files, and
every key: Configuration.