diff --git a/deploy/sentry-slack-alert.sh b/deploy/sentry-slack-alert.sh index 54ddc56..d98953a 100755 --- a/deploy/sentry-slack-alert.sh +++ b/deploy/sentry-slack-alert.sh @@ -2,7 +2,7 @@ # Route every wizwar 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] +# SENTRY_TOKEN=sntryu_... SLACK_CHANNEL_ID=C... deploy/sentry-slack-alert.sh [#channel] # 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. @@ -14,27 +14,22 @@ CHANNEL="${1:-#wizwar-notifications}" API="https://us.sentry.io/api/0/organizations/$ORG" AUTH="Authorization: Bearer $SENTRY_TOKEN" -echo "looking up the Slack integration…" -SLACK_ID=$(curl -s "$API/integrations/?provider_key=slack" -H "$AUTH" | python3 -c ' +# What the API wants, learned the hard way: the Slack action needs the +# channel's ID as well as its name, the project's detector is found by +# project id, and the binding is a PUT of the workflow with detectorIds. +SLACK_ID="${SENTRY_SLACK_INTEGRATION:-345334}" +CHANNEL_ID="${SLACK_CHANNEL_ID:?SLACK_CHANNEL_ID=C... is required (the Slack ID of the channel)}" +PROJECT_ID="${SENTRY_PROJECT_ID:-4512011462049793}" +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) -rows = rows if isinstance(rows, list) else [] -for i in rows: - if i.get("provider", {}).get("key") == "slack" and i.get("status") == "active": - print(i["id"]); break') -if [ -z "$SLACK_ID" ]; then - echo "no active Slack integration on the org — connect Slack first at https://$ORG.sentry.io/settings/integrations/slack/" >&2 - exit 1 -fi -echo "Slack integration $SLACK_ID; project $PROJECT; channel $CHANNEL" +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; } -PROJECT_ID=$(curl -s "$API/projects/" -H "$AUTH" | python3 -c ' -import json, sys -for p in json.load(sys.stdin): - if p.get("slug") == sys.argv[1]: print(p["id"]); break' "$PROJECT") -[ -n "$PROJECT_ID" ] || { echo "project $PROJECT not found in $ORG" >&2; exit 1; } - -PAYLOAD=$(CHANNEL="$CHANNEL" SLACK_ID="$SLACK_ID" python3 -c ' +PAYLOAD=$(CHANNEL="$CHANNEL" CHANNEL_ID="$CHANNEL_ID" SLACK_ID="$SLACK_ID" DETECTOR_ID="$DETECTOR_ID" python3 -c ' import json, os print(json.dumps({ "name": "Wiz-War → Slack", @@ -57,10 +52,11 @@ print(json.dumps({ "type": "slack", "integrationId": int(os.environ["SLACK_ID"]), "data": {}, - "config": {"targetType": "specific", "targetIdentifier": None, "targetDisplay": os.environ["CHANNEL"]}, + "config": {"targetType": "specific", "targetIdentifier": os.environ["CHANNEL_ID"], "targetDisplay": os.environ["CHANNEL"]}, "status": "active", }], }], + "detectorIds": [int(os.environ["DETECTOR_ID"])], }))') echo "creating the workflow…" @@ -72,19 +68,6 @@ if [ "$CODE" != "201" ]; then fi WORKFLOW_ID=$(printf '%s' "$BODY" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("id",""))') echo "created workflow $WORKFLOW_ID" - -# Bind it to the project's issues via a detector connection, so it fires -# for wizwar rather than sitting unattached. -echo "connecting it to $PROJECT's issues…" -DETECTOR_ID=$(curl -s "$API/detectors/?project=$PROJECT_ID&type=error" -H "$AUTH" | python3 -c ' -import json, sys -rows = json.load(sys.stdin) -rows = rows if isinstance(rows, list) else [] -for d in rows: - if d.get("type") in ("error", "issue"): print(d["id"]); break') -if [ -n "$DETECTOR_ID" ]; then - curl -s -o /dev/null -w "detector link: %{http_code}\n" -X PUT "$API/detectors/$DETECTOR_ID/workflows/" -H "$AUTH" -H "Content-Type: application/json" -d "{\"workflowIds\": [$WORKFLOW_ID]}" -else - echo "no error detector found for $PROJECT; connect the workflow to the project in the Alerts UI" >&2 -fi +# A fresh workflow may come back without its detectors; bind them by PUT. +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/"