Migrating from Invoke
Invoke (pyinvoke.org) is a Python library for managing shell tasks. Jake provides similar functionality without requiring Python.
Syntax Comparison
Section titled “Syntax Comparison”| Invoke | Jake |
|---|---|
tasks.py | Jakefile |
@task decorator | task name: |
c.run("cmd") | Direct command |
@task(pre=[dep]) | [dep] |
invoke task | jake task |
Basic Task Conversion
Section titled “Basic Task Conversion”Invoke
Section titled “Invoke”from invoke import task
@taskdef build(c): """Build the application""" c.run("go build -o app")# Build the applicationtask build: go build -o appDependencies
Section titled “Dependencies”Invoke
Section titled “Invoke”from invoke import task
@taskdef clean(c): c.run("rm -rf dist/")
@taskdef install(c): c.run("pip install -r requirements.txt")
@task(pre=[clean, install])def build(c): c.run("python setup.py build")task clean: rm -rf dist/
task install: pip install -r requirements.txt
task build: [clean, install] python setup.py buildParameters
Section titled “Parameters”Invoke
Section titled “Invoke”from invoke import task
@taskdef deploy(c, env="staging", force=False): """Deploy to environment""" cmd = f"./deploy.sh {env}" if force: cmd += " --force" c.run(cmd)Usage: invoke deploy --env=production --force
task deploy env="staging" force="": @desc "Deploy to environment" @if eq({{force}}, "true") ./deploy.sh {{env}} --force @else ./deploy.sh {{env}} @endUsage: jake deploy env=production force=true
Boolean Flags
Section titled “Boolean Flags”Invoke
Section titled “Invoke”@taskdef test(c, verbose=False, coverage=False): cmd = "pytest" if verbose: cmd += " -v" if coverage: cmd += " --cov" c.run(cmd)task test verbose="" coverage="": @if eq({{verbose}}, "true") @if eq({{coverage}}, "true") pytest -v --cov @else pytest -v @end @else @if eq({{coverage}}, "true") pytest --cov @else pytest @end @endOr simpler with positional args:
task test: pytest {{$@}}Usage: jake test -v --cov
Namespaces (Collections)
Section titled “Namespaces (Collections)”Invoke
Section titled “Invoke”from invoke import task, Collection
@taskdef build(c): c.run("docker build -t myapp .")
@taskdef push(c): c.run("docker push myapp")
docker = Collection("docker")docker.add_task(build)docker.add_task(push)
ns = Collection()ns.add_collection(docker)Usage: invoke docker.build
task build: docker build -t myapp .
task push: docker push myapp# Jakefile@import "docker.jake" as docker
task release: [docker.build, docker.push] echo "Released!"Usage: jake docker.build
Context and Configuration
Section titled “Context and Configuration”Invoke
Section titled “Invoke”from invoke import task, Config
config = Config(defaults={ "app": {"name": "myapp", "port": 8080}})
@taskdef run(c): c.run(f"./server --port {c.config.app.port}")app_name = "myapp"port = "8080"
task run: ./server --port {{port}}With environment files:
@dotenv
task run: ./server --port $PORTError Handling
Section titled “Error Handling”Invoke
Section titled “Invoke”@taskdef deploy(c): result = c.run("./deploy.sh", warn=True) if result.failed: print("Deploy failed, rolling back...") c.run("./rollback.sh")task deploy: @ignore ./deploy.sh || ./rollback.shOr with hooks:
@on_error ./rollback.sh
task deploy: ./deploy.shWorking Directory
Section titled “Working Directory”Invoke
Section titled “Invoke”@taskdef build_frontend(c): with c.cd("frontend"): c.run("npm run build")task build-frontend: @cd frontend npm run buildEnvironment Variables
Section titled “Environment Variables”Invoke
Section titled “Invoke”@taskdef build(c): c.run("go build", env={"CGO_ENABLED": "0"})task build: CGO_ENABLED=0 go buildOr globally:
@export CGO_ENABLED=0
task build: go buildComplete Migration Example
Section titled “Complete Migration Example”Before (tasks.py)
Section titled “Before (tasks.py)”from invoke import task, Collection
@taskdef clean(c): """Remove build artifacts""" c.run("rm -rf dist/ build/ *.egg-info")
@taskdef install(c): """Install dependencies""" c.run("pip install -r requirements.txt")
@task(pre=[install])def build(c): """Build the package""" c.run("python setup.py build")
@task(pre=[build])def test(c, verbose=False): """Run tests""" cmd = "pytest tests/" if verbose: cmd += " -v" c.run(cmd)
@task(pre=[test])def deploy(c, env="staging"): """Deploy to environment""" if env == "production": if not input("Deploy to PRODUCTION? (yes/no): ") == "yes": return c.run(f"./deploy.sh {env}")
# Docker namespace@taskdef docker_build(c): c.run("docker build -t myapp .")
@taskdef docker_push(c): c.run("docker push myapp")
docker = Collection("docker")docker.add_task(docker_build, "build")docker.add_task(docker_push, "push")
ns = Collection()ns.add_task(clean)ns.add_task(install)ns.add_task(build)ns.add_task(test)ns.add_task(deploy)ns.add_collection(docker)After (Jakefile)
Section titled “After (Jakefile)”task clean: @desc "Remove build artifacts" rm -rf dist/ build/ *.egg-info
task install: @desc "Install dependencies" pip install -r requirements.txt
task build: [install] @desc "Build the package" python setup.py build
task test verbose="": [build] @desc "Run tests" @if eq({{verbose}}, "true") pytest tests/ -v @else pytest tests/ @end
task deploy env="staging": [test] @desc "Deploy to environment" @if eq({{env}}, "production") @confirm "Deploy to PRODUCTION?" @end ./deploy.sh {{env}}
@group dockertask docker-build: @desc "Build Docker image" docker build -t myapp .
@group dockertask docker-push: @desc "Push Docker image" docker push myappCLI Comparison
Section titled “CLI Comparison”| Invoke | Jake |
|---|---|
invoke | jake |
invoke build | jake build |
invoke -l | jake --list |
invoke deploy --env=prod | jake deploy env=prod |
invoke docker.build | jake docker-build |
invoke -e | Uses environment |
Key Differences
Section titled “Key Differences”| Feature | Invoke | Jake |
|---|---|---|
| Language | Python | DSL |
| Dependencies | pip install | Single binary |
| File tracking | Manual | Native file recipes |
| Parallel | Manual threads | -j flag |
| Watch mode | External tool | Built-in |
What You Gain
Section titled “What You Gain”- No Python dependency - Single binary
- File dependency tracking - Built-in
- Parallel execution -
-jflag - Watch mode -
jake -w - Faster startup - No interpreter
What You Lose
Section titled “What You Lose”- Python flexibility - Move complex logic to scripts
- Programmatic access - No Python API
- Fabric integration - Use shell commands
See Also
Section titled “See Also”- Variables - Configuration
- Conditionals - Logic in recipes
- Imports - Namespacing