Pre/post hooks run before and after tasks. Post-hooks always run—even on failure—making them perfect for cleanup. Neither Make nor Just has this.
@on_error for failure notifications # Global hooks - run for every task
@pre echo "Starting..."
@post echo "Complete!"
# Per-task hooks
task test:
@pre docker-compose up -d
npm test
@post docker-compose down
# Targeted hooks - affect specific tasks
@before deploy ./check-health.sh
@after deploy ./notify-team.sh
# Error hooks - run on failure
@on_error notify-slack "Build failed!"
Without hooks, cleanup code after a failing command never runs.
docker-compose down after a failed test?
It won't execute. Your container keeps running. Hooks guarantee cleanup runs.
Hooks run in a specific, predictable order. Post-hooks always run.
$ jake test
# @pre hook runs first
→ docker-compose up -d
✓ Database started
# Task commands run
→ npm test
✗ 2 tests failed
# @post hook STILL runs (cleanup guaranteed)
→ docker-compose down
✓ Database stopped
# @on_error hook runs because task failed
→ notify-slack "Build failed!"
✓ Notification sent Different hooks for different needs.
@pre / @post Inside Task Declared inside a task definition. Run immediately before/after that task's commands.
task test:
@pre echo "Setup"
npm test
@post echo "Cleanup" @before / @after Targeted Declared outside tasks. Target specific tasks by name without modifying them.
@before deploy ./backup.sh
@after deploy ./notify.sh
task deploy:
kubectl apply -f k8s/ @pre / @post Global Declared at the top level (outside any task). Run for every task.
@pre echo "Starting Jake..."
@post echo "Jake complete!"
task build:
cargo build @on_error Error Handler Runs only when a task fails. Perfect for notifications and alerts.
@on_error notify-slack "Build failed!"
@on_error echo "Check logs at /var/log/build.log" Real-world patterns for hooks.
Start database before tests, stop after—even if tests fail.
@before test docker-compose up -d postgres
@after test docker-compose down
task test:
npm test Notify team on deploy start/end, alert on failure.
task deploy:
@pre ./notify.sh "Deploying..."
kubectl apply -f k8s/
@post ./notify.sh "Deployed!"
@on_error ./alert-pagerduty.sh Run safety checks before deployment without cluttering the task.
@before deploy ./backup-database.sh
@before deploy ./verify-main-branch.sh
task deploy:
./deploy.sh Acquire locks before, release after—guaranteed cleanup.
task release:
@pre ./acquire-lock.sh
./build-release.sh
@post ./release-lock.sh # Always runs! Neither Make nor Just has hooks. Here's what you'd have to do without them.
# Shell workaround - fragile!
test:
docker-compose up -d && \
npm test; \
EXIT=$$?; \
docker-compose down; \
exit $$EXIT task test:
@pre docker-compose up -d
npm test
@post docker-compose down