Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141G6xqLeNRYEtviLWSB5Up
76 lines
3.5 KiB
Bash
Executable File
76 lines
3.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# Route every __NAME__ issue to a Slack channel: new issues (any level or
|
|
# priority), regressions, and reappearances. Creates one Sentry workflow
|
|
# via the alerts API. The token never leaves your shell:
|
|
# SENTRY_TOKEN=sntryu_... deploy/sentry-slack-alert.sh [#channel]
|
|
# The channel defaults to #__SLUG__-notifications (__SLACK_CHANNEL_ID__); pass
|
|
# SLACK_CHANNEL_ID with the channel name to route elsewhere.
|
|
# The token needs the alerts:write scope (an org auth token from
|
|
# https://locallygrownnet.sentry.io/settings/auth-tokens/), and Slack must
|
|
# already be connected to the org under Settings → Integrations.
|
|
set -euo pipefail
|
|
: "${SENTRY_TOKEN:?SENTRY_TOKEN=sntryu_... is required (alerts:write)}"
|
|
ORG="${SENTRY_ORG:-locallygrownnet}"
|
|
PROJECT="${SENTRY_PROJECT:-__SLUG__}"
|
|
CHANNEL="${1:-#__SLUG__-notifications}"
|
|
API="https://us.sentry.io/api/0/organizations/$ORG"
|
|
AUTH="Authorization: Bearer $SENTRY_TOKEN"
|
|
|
|
# The Slack action takes the channel's ID as well as its name; the
|
|
# project's error detector is looked up by project id; a workflow is bound
|
|
# to its detectors by PUT after creation.
|
|
SLACK_ID="${SENTRY_SLACK_INTEGRATION:-__SENTRY_SLACK_INTEGRATION__}"
|
|
CHANNEL_ID="${SLACK_CHANNEL_ID:-__SLACK_CHANNEL_ID__}"
|
|
PROJECT_ID="${SENTRY_PROJECT_ID:-__SENTRY_PROJECT_ID__}"
|
|
echo "Slack integration $SLACK_ID; project $PROJECT_ID; channel $CHANNEL ($CHANNEL_ID)"
|
|
|
|
DETECTOR_ID=$(curl -s "$API/detectors/?project=$PROJECT_ID" -H "$AUTH" | python3 -c '
|
|
import json, sys
|
|
rows = json.load(sys.stdin)
|
|
for d in (rows if isinstance(rows, list) else []):
|
|
if str(d.get("projectId")) == sys.argv[1] and d.get("type") == "error": print(d["id"]); break' "$PROJECT_ID")
|
|
[ -n "$DETECTOR_ID" ] || { echo "no error detector for project $PROJECT_ID" >&2; exit 1; }
|
|
|
|
PAYLOAD=$(CHANNEL="$CHANNEL" CHANNEL_ID="$CHANNEL_ID" SLACK_ID="$SLACK_ID" DETECTOR_ID="$DETECTOR_ID" python3 -c '
|
|
import json, os
|
|
print(json.dumps({
|
|
"name": "__NAME__ → Slack",
|
|
"enabled": True,
|
|
"environment": None,
|
|
"config": {"frequency": 0},
|
|
"triggers": {
|
|
"logicType": "any-short",
|
|
"conditions": [
|
|
{"type": "first_seen_event", "comparison": True, "conditionResult": True},
|
|
{"type": "regression_event", "comparison": True, "conditionResult": True},
|
|
{"type": "reappeared_event", "comparison": True, "conditionResult": True},
|
|
],
|
|
"actions": [],
|
|
},
|
|
"actionFilters": [{
|
|
"logicType": "all",
|
|
"conditions": [],
|
|
"actions": [{
|
|
"type": "slack",
|
|
"integrationId": int(os.environ["SLACK_ID"]),
|
|
"data": {},
|
|
"config": {"targetType": "specific", "targetIdentifier": os.environ["CHANNEL_ID"], "targetDisplay": os.environ["CHANNEL"]},
|
|
"status": "active",
|
|
}],
|
|
}],
|
|
"detectorIds": [int(os.environ["DETECTOR_ID"])],
|
|
}))')
|
|
|
|
echo "creating the workflow…"
|
|
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "$API/workflows/" -H "$AUTH" -H "Content-Type: application/json" -d "$PAYLOAD")
|
|
CODE=$(printf '%s' "$RESPONSE" | tail -1)
|
|
BODY=$(printf '%s' "$RESPONSE" | sed '$d')
|
|
if [ "$CODE" != "201" ]; then
|
|
echo "Sentry answered $CODE:" >&2; echo "$BODY" >&2; exit 1
|
|
fi
|
|
WORKFLOW_ID=$(printf '%s' "$BODY" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("id",""))')
|
|
echo "created workflow $WORKFLOW_ID"
|
|
# Binding to the detector is done by PUT, the whole workflow resent.
|
|
curl -s -o /dev/null -w "bound to detector $DETECTOR_ID: %{http_code}\n" -X PUT "$API/workflows/$WORKFLOW_ID/" -H "$AUTH" -H "Content-Type: application/json" -d "$PAYLOAD"
|
|
echo "done: https://$ORG.sentry.io/monitors/alerts/$WORKFLOW_ID/"
|