Overview
Overview
Usage
Usage
Examples
Examples
GitHub
Plugins

slack

The Slack plugin - send notification to Slack via WebHook.

Notifications to a Slack via Slack incoming webhook

The plugin ships in the default whoosh binary. If you use whoosh-core, add it to a custom build:

whoosh build --with github.com/yousysadmin/whoosh/plugins/slack -o ./whoosh

(see Installation -> With custom plugins) - then list it under plugins:.

Usage

plugins:
  - name: slack
    params:
      webhook_url: '{{ env "SLACK_WEBHOOK_URL" }}'   # required
      channel: "#deploys"                            # optional, legacy webhooks only
      notify_rollback: true                          # off by default

With just that, every whoosh <stage> deploy posts:

  • started (blue, before deploy:starting) - app, stage, release id
  • succeeded (green, after deploy:finished) - app, stage, commit, release id, duration
  • failed (red, after deploy:failed) - app, stage, the failure message, duration
  • rolled back (yellow, after deploy:rollback) - opt-in via notify_rollback: true

Notifications fire only during the deploy lifecycle (never for config, run, or standalone tasks) and are best-effort: a Slack outage is logged as a warning and never fails (or “un-succeeds”) a deploy.

Params

ParamDefaultDescription
webhook_url- (required)Incoming-webhook URL, the default for every notification. Registered as a masked secret, typically '{{ env "SLACK_WEBHOOK_URL" }}'.
webhook_start / webhook_success / webhook_fail / webhook_rollbackwebhook_urlPer-event webhook overrides - e.g. route failures to an alerts channel’s webhook while everything else uses the default.
channelwebhook defaultOverride the target channel. Honored by legacy webhooks only - Slack-app webhooks ignore it.
usernamewebhook defaultOverride the sender name (legacy webhooks only).
icon_emojiwebhook defaultOverride the sender icon, e.g. ":package:" (legacy webhooks only).
notify_starttruePost when a deploy starts.
notify_successtruePost when a deploy finishes successfully.
notify_failtruePost when a deploy fails (this is also what enables the deploy:failed hook).
notify_rollbackfalsePost after whoosh <stage> deploy:rollback.
message_start / message_success / message_fail / message_rollbackbuilt-inPer-event message template overrides (see below).
color_start / color_success / color_fail / color_rollbackbuilt-inPer-event attachment-bar color overrides: good, warning, danger, or #rrggbb.
rich_fieldsfalseStructured success/fail message: a fields table with User, Stage, Branch, Revision, Duration, and Release (the release path, the duration moves from the text into the table).
changelogdisabledPost the commits between the previously deployed revision and the new one on the success notification (see below).
deployer_github_lookupfalseResolve the deployer to their GitHub display name when it looks like a login (e.g. GITHUB_ACTOR in CI). One unauthenticated API call per process, any failure falls back to the login. Used in the rich_fields User field.
timeout"10s"Bound on each webhook POST (Go duration).

Message templates

Messages are Go templates rendered with the full deploy context - {{.app_name}}, {{.stage}}, {{.commit_hash}}, {{.release_timestamp}}, {{.error}} (in the fail message), your vars, sprig helpers, Slack mrkdwn. Defaults:

started:   :rocket: *{{.app_name}}* deploy to *{{.stage}}* started (release {{.release_timestamp}})
succeeded: :white_check_mark: *{{.app_name}}* deployed to *{{.stage}}* ({{ trunc 7 .commit_hash }}, release {{.release_timestamp}})
failed:    :x: *{{.app_name}}* deploy to *{{.stage}}* failed: {{.error}}
rollback:  :leftwards_arrow_with_hook: *{{.app_name}}* on *{{.stage}}* rolled back

The succeeded/failed messages get the deploy duration appended automatically ( in 42s). The start message can’t reference {{.commit_hash}} - it is resolved later, at deploy:updating.

Escaping caveat: like every params: value, a message_* override is also template-rendered once at load, where runtime keys render empty. Escape them so they survive to run time:

message_fail: '{{ "{{ .app_name }} broke on {{ .stage }}: {{ .error }}" }}'

Changelog

With changelog.enabled: true the success notification also posts what changed: the commits between the previously deployed revision and the new one (whoosh core’s {{.changelog}} deploy-context value, captured from the repo mirror at deploy:updating), one attachment per commit - author, subject linked to the commit, and an optional @mention when the author’s email is mapped to a Slack member ID:

plugins:
  - name: slack
    params:
      webhook_url: '{{ env "SLACK_WEBHOOK_URL" }}'
      rich_fields: true
      changelog:
        enabled: true
        max_commits: 20                # default 20, capped at 100
        # commit_url: ""               # optional: a prefix the SHA is appended to, or a "{hash}" template;
                                       # empty derives https://<host>/<org>/<repo>/commit/ from app.repo
        authors:                       # commit author email -> Slack member ID (mentioned on the commit)
          [email protected]: U0123ABCD
          [email protected]: U0456EFGH

Notes:

  • The commits come from the core {{.changelog}} value, so the plugin runs no git itself - and the changelog is empty on the first deploy, when both revisions match, outside a deploy, and on a whoosh core without the changelog context key.
  • Redeploying the revision that is already live posts the summary with an explicit “No changes since the previous release” note; the other empty cases post the plain summary.
  • max_commits caps how many of the captured commits are displayed (core captures up to 100).
  • Slack limits a message to 20 attachments: the summary plus the first 19 commits go in one message, the rest follow as continuation messages.
  • Mentions render in each commit’s text line - Slack does not render mrkdwn inside attachment titles.
  • Everything is best-effort: whatever goes wrong, the plain summary is posted and the deploy never fails because of the changelog.

Custom message

Post a custom message from any task or hook:

tasks:
  announce-migrations:
    action: slack:send
    with:
      message: "Running migrations on *{{.stage}}* ({{ trunc 7 .commit_hash }})"
      color: warning
      optional: true            # don't fail the deploy if Slack is down

hooks:
  before:
    deploy:publishing: [ announce-migrations ]

with: fields (all except message optional):

FieldDescription
messageThe text to post (required). Rendered with the full deploy context, Slack mrkdwn works.
colorAttachment bar: good, warning, danger, or #rrggbb. Empty = no bar.
optionaltrue makes delivery failures non-fatal (warned instead of failing the task).
webhook_urlRewrite the webhook for this task only (beats the per-event and global params) - e.g. post to another workspace/channel’s webhook.
channel, username, icon_emojiPer-call overrides of the plugin params (legacy webhooks only).

A literal with: webhook_url value is registered as a masked secret at load, so it is redacted even in --dry-run plans. For a templated value, read it with envSecret so it is masked from the moment it renders:

with:
  webhook_url: '{{ envSecret "SLACK_TASK_WEBHOOK" }}'

A plain slack:send task fails on delivery errors (so a notification you explicitly asked for isn’t silently lost), set optional: true for fire-and-forget. Actions run operator-side - nothing is executed on the hosts. Under --dry-run the action prints its plan and posts nothing.

Notes

  • Reserved task names: the plugin contributes hidden tasks slack:notify-start, slack:notify-success, slack:notify-fail, slack:notify-rollback - your task with one of these names would be overwritten.
  • Masking: the webhook URL is registered as a secret (on top of the built-in hooks.slack.com pattern), so it is redacted from echoed commands, output, logs, and dry-run plans, errors never embed the URL.
  • Per-stage gating: like any plugin, use only:/except:/enabled: on the spec to notify from some stages only.
  • whoosh <stage> validate is offline and never contacts Slack, config/run/deploy do load the plugin, so a missing webhook_url (e.g. unset env var) fails fast with a pointer to the fix.