Hooks & phases
Attach tasks to the deploy lifecycle with before/after hooks, the special deploy:failed and deploy:rollback hooks, and phase replacement.
A deployment runs a fixed sequence of phases.
You attach tasks to a phase with hooks.before / hooks.after, keyed by phase name:
hooks:
after:
deploy:updated: [ bundle, assets, migrate ] # release built, not yet live
deploy:published: [ restart, healthcheck ] # release is now live
The phase order:
deploy:starting ---> deploy:check ---> deploy:init ---> deploy:started
----------------------------------+------------------------------------
|
v
deploy:updating ---> deploy:symlink ---> deploy:updated ---> deploy:publishing
--------------------------------------+---------------------------------------
|
v
deploy:published ---> deploy:finishing ---> deploy:finished
-----------------------------------------------------------The marker phases (
deploy:init,deploy:started,deploy:updated,deploy:published,deploy:finished) run no built-in command - they are stable hook anchors. Prefer these for your own hooks:deploy:initto provision the host (see below),deploy:updatedfor “release built but not yet live” (bundle/assets/migrate), anddeploy:publishedfor “release is live” (restart/healthcheck) - rather than binding to internal step names likesymlink/publishing. For a marker phase,beforeandafterare the same point.deploy:initfires right after the directory tree is ensured but before the release is built - the place to install OS packages, ensure a language runtime, or otherwise prepare the host. Because the release directory doesn’t exist yet, give such a task an explicitdir:pointing at an existing path (dir:is Go-templated, sodir: "{{.deploy_to}}"works):tasks: provision: desc: Install OS packages the app needs roles: [app, web] dir: "{{.deploy_to}}" scripts: - path: provision.sh interpreter: /usr/bin/env bash hooks: before: deploy:init: [provision]deploy:failed- a special key whoseaftertasks run when a deploy errors (the deploy still fails afterward). The failure message is exposed as{{.error}}/$DEPLOY_ERROR. Good for failure notifications.deploy:rollback-before/aftertasks wrap the symlink swap ofwhoosh <stage> deploy:rollback, andaftertasks run withcurrentalready repointed at the restored release. Seeexamples/07-rails-assets. A task can also replace the built-in swap entirely withreplace: deploy:rollback(e.g. anaws:ec2:asg:rollbackaction), sowhoosh <stage> deploy:rollbackdoes the app-specific thing while staying one command. The hooks still run around it, and--cleanupdoesn’t apply to a replaced rollback.
Task-to-task hooks
A hook key can also be a task name instead of a phase. The named tasks then run before / after that task every
time it runs - whether invoked directly (whoosh <stage> <task>), pulled in as a deps: dependency, or fired from
another hook - without binding either task to a phase:
hooks:
after:
restart_sidekiq: [ push_to_newrelic ] # whenever restart_sidekiq runs, notify afterward
before:
restart_sidekiq: [ drain_queues ] # ...and drain first
Ordering around the wrapped task: its before hooks run first, then its own deps:, then the task body, then its
after hooks. A hook that loops back to the task it wraps is rejected as a dependency cycle.
This is the difference from deps:: a dependency is declared on the consuming task and only pulls work in before
it, whereas an after: task hook lets an unrelated task attach work after another without editing it.
A hook task can read which phase it is running for via {{.phase}} / $DEPLOY_PHASE, so one templated script can
handle start / success / failure - see
examples/06-slack-notify.
See Usage -> Deploy lifecycle for what each phase does.