Recipe: Slack notifier hook¶
Problem¶
You want the team to know when someone's environment is up. Maybe to welcome a new contributor, maybe to flag drift, maybe just so the lead engineer can sip coffee and watch onboardings tick by. Send a Slack message at the end of jarvy setup — without making it spam or blocking the install on Slack outages.
Config¶
[hooks]
post_setup = """
# Skip in CI — only notify on real laptops.
if [ -n "${CI:-}" ]; then exit 0; fi
# Slack webhook URL is per-team; sourced from env, never hardcoded.
if [ -z "${JARVY_SLACK_WEBHOOK:-}" ]; then exit 0; fi
USER_NAME="${JARVY_USER:-${USER:-someone}}"
HOST_NAME="$(hostname -s)"
PROJECT="$(basename "$PWD")"
curl -s -X POST -H 'Content-Type: application/json' \\
--data "{\\"text\\":\\":wave: $USER_NAME finished \\`jarvy setup\\` for \\`$PROJECT\\` on $HOST_NAME\\"}" \\
"$JARVY_SLACK_WEBHOOK" \\
|| true # never fail setup on a Slack outage
exit 0
"""
[hooks.config]
continue_on_error = true # advisory: setup succeeds even if Slack 500s
timeout = 10
Why it works¶
| Piece | What it does |
|---|---|
if [ -n "$CI" ]; then exit 0; fi |
CI runs don't notify — only laptop runs do. |
if [ -z "$JARVY_SLACK_WEBHOOK" ]; then exit 0; fi |
If the webhook isn't set, the hook is a silent no-op. Developers who haven't opted in get nothing. |
\|\| true |
Slack down? Setup still succeeds. The notifier is advisory. |
continue_on_error = true |
Belt and suspenders — even if curl errors, setup keeps going. |
timeout = 10 |
A misbehaving Slack endpoint can't hang setup forever. |
Each developer sets JARVY_SLACK_WEBHOOK in their shell rc:
Or stash it in 1Password and pull it via op — see Secrets via 1Password.
Variations¶
Notify only on first setup, not re-runs:
Notify on drift instead of setup:
[hooks]
post_setup = """
DRIFT=$(jarvy drift check --format json 2>/dev/null || true)
if echo "$DRIFT" | grep -q '"drifted": true'; then
curl ... "drift detected on $USER's $HOST"
fi
"""
Discord / Teams / Mattermost: same pattern, different webhook URL and JSON shape. Most chat platforms accept { "text": "..." }.
Channel routing by project:
case "$(basename $PWD)" in
frontend-*) CHANNEL="#frontend-onboarding" ;;
backend-*) CHANNEL="#backend-onboarding" ;;
*) CHANNEL="#general-onboarding" ;;
esac
Then post to a Slack app that routes by channel name in the JSON.
Track time-to-setup as a metric:
DURATION=$(jq '.duration_seconds' .jarvy/state.json)
curl ... "$USER finished setup in ${DURATION}s"
Caveats¶
- Webhook URLs are secrets. Don't commit them. Use the env-var pattern.
- Don't notify in CI. New runners on every PR = Slack channel firehose. Always guard with
if [ -n "$CI" ]; then exit 0; fi. - Rate limits matter. Slack incoming webhooks cap at 1 message/sec. If your hook fires for many tools, batch into one message.
- Privacy: the message includes
$USERand hostname. If your team is privacy-conscious, drop those.
See also¶
- Hooks guide
- Hook execution model
- 1Password recipe — for the webhook URL