diff --git a/scripts/ci/notify-telegram.sh b/scripts/ci/notify-telegram.sh new file mode 100755 index 00000000..b3d9d620 --- /dev/null +++ b/scripts/ci/notify-telegram.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# notify-telegram.sh — post a Telegram message for a CI stage. +# +# Usage: notify-telegram.sh [--dry-run] [] +# +# Env (required unless --dry-run): +# TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID +# Env (always read for context): +# GITHUB_REPOSITORY, GITHUB_RUN_ID, GITHUB_SERVER_URL, GITHUB_SHA, GITHUB_WORKFLOW +set -euo pipefail + +DRY_RUN=0 +if [ "${1:-}" = "--dry-run" ]; then + DRY_RUN=1 + shift +fi + +VERB="${1:-}" +STAGE="${2:-}" +EXTRA="${3:-}" + +case "$VERB" in + start|ok|fail) ;; + *) echo "usage: $0 [--dry-run] []" >&2; exit 2 ;; +esac + +[ -n "$STAGE" ] || { echo "usage: $0 [--dry-run] []" >&2; exit 2; } + +if [ "$DRY_RUN" -eq 0 ]; then + : "${TELEGRAM_BOT_TOKEN:?TELEGRAM_BOT_TOKEN required}" + : "${TELEGRAM_CHAT_ID:?TELEGRAM_CHAT_ID required}" +fi + +REPO="${GITHUB_REPOSITORY:-unknown/repo}" +RUN_ID="${GITHUB_RUN_ID:-0}" +SERVER="${GITHUB_SERVER_URL:-https://git.gnerim.ru}" +SHA="${GITHUB_SHA:-unknown}" +SHORT_SHA="${SHA:0:7}" +RUN_URL="${SERVER}/${REPO}/actions/runs/${RUN_ID}" + +case "$VERB" in + start) ICON="🚀"; HEAD="${ICON} ${STAGE} started" ;; + ok) ICON="✅"; HEAD="${ICON} ${STAGE} passed" ;; + fail) ICON="❌"; HEAD="${ICON} ${STAGE} FAILED${EXTRA:+ at step \"${EXTRA}\"}" ;; +esac + +# Body is plain text (no HTML escaping needed for our content). +BODY="${HEAD} +commit: ${SHORT_SHA} +gitea run: ${RUN_URL}" + +if [ "$DRY_RUN" -eq 1 ]; then + printf '%s\n' "$BODY" + exit 0 +fi + +# Send via curl. Use --data-urlencode to avoid encoding pitfalls. +curl -fsS -X POST \ + "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \ + --data-urlencode "chat_id=${TELEGRAM_CHAT_ID}" \ + --data-urlencode "text=${BODY}" \ + --data-urlencode "disable_web_page_preview=true" \ + >/dev/null diff --git a/tests/ci/test-notify-telegram.sh b/tests/ci/test-notify-telegram.sh new file mode 100755 index 00000000..c9a816ce --- /dev/null +++ b/tests/ci/test-notify-telegram.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# Test: scripts/ci/notify-telegram.sh in --dry-run mode emits the right payloads. +set -euo pipefail + +SCRIPT="$(cd "$(dirname "$0")/../.." && pwd)/scripts/ci/notify-telegram.sh" +[ -x "$SCRIPT" ] || { echo "FAIL: $SCRIPT not executable"; exit 1; } + +# Required env for non-dry-run; test still sets them so the script doesn't bail early. +export TELEGRAM_BOT_TOKEN="test-token" +export TELEGRAM_CHAT_ID="123" +export GITHUB_REPOSITORY="gnezim/flights-web" +export GITHUB_RUN_ID="42" +export GITHUB_SERVER_URL="https://git.gnerim.ru" +export GITHUB_SHA="abc1234567890" +export GITHUB_WORKFLOW="ci-deploy" + +assert_contains() { + local haystack="$1" needle="$2" + case "$haystack" in + *"$needle"*) ;; + *) echo "FAIL: expected to find '$needle' in:"; echo "$haystack"; exit 1 ;; + esac +} + +# --- start --- +out=$("$SCRIPT" --dry-run start ci-deploy) +assert_contains "$out" "🚀 ci-deploy started" +assert_contains "$out" "abc1234" +assert_contains "$out" "https://git.gnerim.ru/gnezim/flights-web/actions/runs/42" + +# --- ok --- +out=$("$SCRIPT" --dry-run ok ci-deploy) +assert_contains "$out" "✅ ci-deploy passed" + +# --- fail with extra context --- +out=$("$SCRIPT" --dry-run fail ci-deploy "Run Playwright e2e") +assert_contains "$out" "❌ ci-deploy FAILED" +assert_contains "$out" "Run Playwright e2e" + +# --- missing env should error in non-dry-run --- +unset TELEGRAM_BOT_TOKEN +if "$SCRIPT" ok ci-deploy 2>/dev/null; then + echo "FAIL: expected error when TELEGRAM_BOT_TOKEN missing" + exit 1 +fi + +echo "PASS: notify-telegram.sh"