Manifest basics¶
A manifest is one YAML file that describes one CLI. This page explains its parts and how cliffy turns them into Python. For a hands-on introduction, start with the Quickstart.
The smallest manifest¶
Three keys are required: name, version and commands.
Top-level keys¶
| Key | What it is for | Guide |
|---|---|---|
name |
The command people type. | |
version |
Printed by --version. Must be a string, so write "1.0" with quotes. |
|
help |
Description shown at the top of --help. |
|
commands |
The commands and the code they run. | Commands, groups and aliases |
imports |
Python imports for the generated module. | below |
functions |
Helper functions shared by all commands. | below |
vars |
Variables you can reuse across the manifest. | Vars and Jinja |
types |
Named parameter definitions. | Params and types |
global_params |
Params added to commands. | Templates and global params |
command_templates |
Reusable params, hooks and settings. | Templates and global params |
includes |
Other manifests to merge in. | Includes and merging |
requires |
Python packages the CLI depends on. | below |
cli_options |
Options for the CLI application object. | below |
use_click |
Generate Click code instead of Typer. | Click and Typer backends |
tests |
Commands to run and assertions to check. | Testing manifests |
examples |
Example command lines, shown in --help and in generated docs. |
Generating docs |
Keys that cliffy does not know are ignored. The manifest reference lists every key with its type and default.
How a manifest becomes Python¶
cliffy writes a single Python module from the manifest, in this order:
import subprocess,import typerand the lines fromimports- one variable per entry in
vars - the Typer application, with
--versionbuilt in - the
functions - one function per command, registered with Typer
Each command's run script becomes the body of its function, and its params become the function's
arguments. That is the whole model: anything you can write in a Python function, you can write in
run.
To read the generated module for any manifest:
The output is the module itself, so cli render hello.yaml > hello.py writes a file you can run. The
closing # Rendered ... line goes to the error stream and does not end up in that file.
Imports and functions¶
imports: |
import json
from pathlib import Path
functions:
- |
def load(path: str) -> dict:
return json.loads(Path(path).read_text())
- |
def branch() -> str:
out = $ git rev-parse --abbrev-ref HEAD
return out.decode().strip()
imports is a block of text or a list of lines. functions is a list with one function per item.
subprocess and typer are always imported, so typer.Exit, typer.confirm and friends are
available everywhere.
Write functions as a list when a function contains shell lines. A single
block of text is also accepted, but $ lines inside it are not translated.
Dependencies¶
List the packages your code imports under requires, using the same specifiers as pip:
cli load,cli run,cli testandcli validatestop withmissing requirementwhen a listed package is not installed in the environment cliffy runs in.cli builddownloads the listed packages and bundles them into the executable.
CLI options¶
cli_options is passed to the Typer application as keyword arguments. Two common settings:
cli_options:
add_completion: false # hide --install-completion and --show-completion
no_args_is_help: true # print help when the CLI is called without a command
Check, run, load¶
| Command | What it does |
|---|---|
cli validate hello.yaml |
Checks the YAML, the schema, the requires and that the generated code imports. |
cli render hello.yaml |
Prints the generated Python. |
cli run hello.yaml -- <args> |
Runs the CLI once without installing it. |
cli test hello.yaml |
Runs the tests section. |
cli load hello.yaml |
Installs the CLI as a command in the current environment. |
All of them are described in the CLI reference.
YAML pitfalls¶
Manifests mix YAML, Jinja and Python, and each has its own special characters.
A colon followed by a space ends a YAML key. This one-line command breaks, because of : inside
the string:
Use a block instead:
Quote values that YAML would not read as text. version: 1.0 is a number and is rejected;
version: "1.0" works. The same applies to values in vars.
Quote a one-line command that starts with >. YAML treats a leading > as its own syntax, so
write run: "> ls *.txt".
Double braces belong to Jinja. {{ name }} is replaced when the manifest is loaded. To get
literal double braces into your Python, see Vars and Jinja.
Editor support¶
cliffy publishes a JSON schema for manifests, which gives you completion and validation while you type. See Editor setup.