All Features
Jake Exclusive

Hooks

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.

  • Guaranteed cleanup — Post-hooks run even when tasks fail
  • Targeted hooks — Attach to any task without modifying it
  • Error handling@on_error for failure notifications
Jakefile
# 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!"

The Problem with Manual Cleanup

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.

Execution Order

Hooks run in a specific, predictable order. Post-hooks always run.

Order of Execution

1 Global @pre hooks
2 @before hooks targeting this task
3 Task @pre hooks (inside task)
4 Task commands
5 Task @post hooks (inside task)
6 @after hooks targeting this task
7 Global @post hooks
8 @on_error hooks (only if failed)
Output when test fails
$ 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

Hook Types

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"

Use Cases

Real-world patterns for hooks.

Database Testing

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

Deployment Notifications

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

Backup Before Deploy

Run safety checks before deployment without cluttering the task.

@before deploy ./backup-database.sh
@before deploy ./verify-main-branch.sh

task deploy:
    ./deploy.sh

Lock Management

Acquire locks before, release after—guaranteed cleanup.

task release:
    @pre ./acquire-lock.sh
    ./build-release.sh
    @post ./release-lock.sh  # Always runs!

vs Make & Just

Neither Make nor Just has hooks. Here's what you'd have to do without them.

Without hooks
# Shell workaround - fragile!
test:
    docker-compose up -d && \
    npm test; \
    EXIT=$$?; \
    docker-compose down; \
    exit $$EXIT
With Jake hooks
task test:
    @pre docker-compose up -d
    npm test
    @post docker-compose down

Try Hooks

Guaranteed cleanup and error handling for your builds.