Invoke Cheat Sheet
Overview
Invoke is a Python library for managing shell-oriented subprocesses and organizing executable tasks. It serves as both a command-line tool and a Python library, replacing Makefiles and shell scripts with clean Python functions. Invoke is the local-execution component that powers Fabric (which adds SSH capabilities).
Invoke provides task declaration via decorators, argument parsing from the command line, task dependencies, shell command execution with real-time output, and a flexible configuration system. It works on Python 3.6+ across all major operating systems.
Installation
pip install invoke
# Verify
inv --version
invoke --version
Core Usage
Basic tasks.py
from invoke import task
@task
def clean(c):
"""Remove build artifacts."""
c.run("rm -rf build/ dist/ *.egg-info")
c.run("find . -name '*.pyc' -delete")
c.run("find . -name '__pycache__' -type d -exec rm -rf {} +")
@task
def test(c):
"""Run test suite."""
c.run("pytest tests/ -v")
@task
def lint(c):
"""Run linters."""
c.run("ruff check src/")
c.run("mypy src/")
@task(pre=[clean, lint, test])
def build(c):
"""Build the package."""
c.run("python -m build")
@task
def serve(c, port=8000):
"""Start development server."""
c.run(f"python -m http.server {port}")
# Run tasks
inv clean
inv test
inv build
inv serve --port 9000
Core Commands
| Command | Description |
|---|---|
inv --list | List available tasks |
inv <task> | Run a task |
inv <task> --help | Show task help |
inv -e <task> | Echo commands before running |
inv -w <task> | Warn on failure (don’t exit) |
inv -d <task> | Show detailed task info |
Task Configuration
Arguments and Types
from invoke import task
@task
def deploy(c, env='staging', tag='latest', dry_run=False):
"""Deploy application.
Args:
env: Target environment (staging/production)
tag: Docker image tag
dry_run: Show commands without executing
"""
cmd = f"docker pull myapp:{tag}"
if dry_run:
print(f"Would run: {cmd}")
else:
c.run(cmd)
c.run(f"kubectl set image deployment/myapp myapp=myapp:{tag} -n {env}")
@task
def greet(c, name, greeting='Hello'):
"""Greet someone."""
print(f"{greeting}, {name}!")
# Positional argument
@task(positional=['name'])
def hello(c, name):
print(f"Hello {name}!")
inv deploy --env production --tag v2.0
inv deploy -e production -t v2.0
inv deploy --dry-run
inv greet --name World
inv hello Alice
Task Dependencies
from invoke import task
@task
def clean(c):
c.run("rm -rf dist/")
@task
def compile(c):
c.run("python -m compileall src/")
@task(pre=[clean, compile])
def build(c):
"""Build after cleaning and compiling."""
c.run("python -m build")
@task(post=[clean])
def release(c):
"""Release, then clean up."""
c.run("twine upload dist/*")
# Call tasks explicitly
@task
def deploy(c, env='staging'):
build(c) # Run build task first
c.run(f"scp dist/*.whl {env}-server:/opt/app/")
Namespaces
# tasks/docker.py
from invoke import task
@task
def build(c, tag='latest'):
c.run(f"docker build -t myapp:{tag} .")
@task
def push(c, tag='latest'):
c.run(f"docker push myregistry/myapp:{tag}")
# tasks/__init__.py
from invoke import Collection
from tasks import docker, test as test_tasks
ns = Collection()
ns.add_collection(Collection.from_module(docker), name='docker')
ns.add_collection(Collection.from_module(test_tasks), name='test')
inv docker.build --tag v2.0
inv docker.push --tag v2.0
inv test.unit
Configuration
invoke.yaml
run:
echo: true
warn: false
hide: false
pty: false
tasks:
search_root: .
collection_name: tasks
my_project:
deploy_host: web1.example.com
db_host: db.example.com
redis_host: redis.example.com
@task
def deploy(c):
host = c.config.my_project.deploy_host
c.run(f"ssh {host} 'cd /app && git pull'")
Environment-Based Config
# tasks.py
from invoke import task, Config
@task
def show_config(c):
print(f"Deploy host: {c.config.deploy.host}")
print(f"Deploy user: {c.config.deploy.user}")
# invoke.yaml
deploy:
host: staging.example.com
user: deploy
# invoke.production.yaml (override)
deploy:
host: prod.example.com
Advanced Usage
Command Execution Options
@task
def advanced_run(c):
# Capture output
result = c.run("git rev-parse HEAD", hide=True)
commit = result.stdout.strip()
print(f"Current commit: {commit}")
# Check success/failure
result = c.run("test -f config.yaml", warn=True)
if result.failed:
print("Config not found, creating default...")
c.run("cp config.example.yaml config.yaml")
# Dry run pattern
c.run("echo 'would deploy'", dry=True)
# With environment variables
c.run("echo $MY_VAR", env={"MY_VAR": "hello"})
# Using pseudo-terminal
c.run("python manage.py shell", pty=True)
# Timeout
c.run("long-running-command", timeout=60)
Watchers (Auto-Respond to Prompts)
from invoke import task, Responder
@task
def setup(c):
yes_responder = Responder(
pattern=r"Are you sure\?",
response="yes\n",
)
c.run("dangerous-command", watchers=[yes_responder])
sudo_responder = Responder(
pattern=r"\[sudo\] password",
response="mypassword\n",
)
c.run("sudo apt update", watchers=[sudo_responder])
Collection-Level Configuration
from invoke import task, Collection
@task
def build(c):
c.run(f"docker build -t {c.config.image_name} .")
@task
def push(c):
c.run(f"docker push {c.config.image_name}")
ns = Collection(build, push)
ns.configure({
'image_name': 'myregistry/myapp:latest',
})
Error Handling
from invoke import task, UnexpectedExit
@task
def safe_deploy(c):
try:
c.run("pytest tests/")
except UnexpectedExit as e:
print(f"Tests failed with exit code {e.result.exited}")
print("Aborting deployment")
return
c.run("docker build -t myapp .")
c.run("docker push myapp")
Troubleshooting
| Issue | Solution |
|---|---|
| Task not found | Check file is named tasks.py; verify @task decorator |
| Arguments not parsing | Use --arg value format; check type hints |
| Command fails silently | Add echo=True to see commands; remove hide=True |
| Interactive command hangs | Use pty=True for interactive commands |
| Config not loading | Check invoke.yaml location and syntax |
| Import error in tasks | Ensure all dependencies are installed |
# List all tasks
inv --list
# Show task help
inv --help deploy
# Echo commands
inv -e deploy
# Debug configuration
inv --print-completion-script bash
python -c "from invoke import Config; print(Config().as_dict())"