name: Deploy production on: push: branches: - master workflow_dispatch: concurrency: group: production cancel-in-progress: false jobs: deploy: if: gitea.repository == 'Robotail/Corp' && gitea.ref == 'refs/heads/master' runs-on: deploy timeout-minutes: 60 steps: - name: Deploy current Gitea revision to CORP shell: sh env: CORP_DEPLOY_SSH_KEY: ${{ secrets.CORP_DEPLOY_SSH_KEY }} TARGET_SHA: ${{ gitea.sha }} GITEA_RUN_ID: ${{ gitea.run_id }} run: | set -eu umask 077 case "${GITEA_RUN_ID}" in ''|*[!0-9]*) echo 'Invalid run ID.' >&2; exit 2 ;; esac case "${TARGET_SHA}" in *[!0-9a-f]*|'') echo 'Invalid revision.' >&2; exit 2 ;; esac [ "${#TARGET_SHA}" -eq 40 ] || exit 2 status_file="${RUNNER_TEMP:-/tmp}/eusrr-production-deploy-${GITEA_RUN_ID}.status" printf '%s\n' failure > "${status_file}" work_dir="$(mktemp -d)" trap 'rm -rf -- "${work_dir}"' EXIT trap 'exit 129' HUP trap 'exit 130' INT trap 'exit 143' TERM printf '%s\n' "${CORP_DEPLOY_SSH_KEY}" > "${work_dir}/key" unset CORP_DEPLOY_SSH_KEY printf '%s\n' '172.11.0.11 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJsNn4tOkIiZgZCWCumPi/OTuB7kSUkFd8u7iJnIFTwx' > "${work_dir}/known_hosts" ssh \ -i "${work_dir}/key" \ -o BatchMode=yes \ -o IdentitiesOnly=yes \ -o ConnectTimeout=10 \ -o ServerAliveInterval=15 \ -o ServerAliveCountMax=3 \ -o StrictHostKeyChecking=yes \ -o UserKnownHostsFile="${work_dir}/known_hosts" \ deploy@172.11.0.11 \ "deploy ${TARGET_SHA}" printf '%s\n' success > "${status_file}" - name: Send deployment report to Telegram if: always() continue-on-error: true shell: sh env: TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} DEPLOY_SHA: ${{ gitea.sha }} DEPLOY_REF: ${{ gitea.ref_name }} DEPLOY_REPOSITORY: ${{ gitea.repository }} DEPLOY_ACTOR: ${{ gitea.actor }} GITEA_SERVER_URL: ${{ gitea.server_url }} GITEA_RUN_ID: ${{ gitea.run_id }} GITEA_RUN_NUMBER: ${{ gitea.run_number }} run: | # Reporting must never change the deployment result. set +e umask 077 status_file="${RUNNER_TEMP:-/tmp}/eusrr-production-deploy-${GITEA_RUN_ID}.status" if [ -r "${status_file}" ]; then DEPLOY_OUTCOME="$(cat "${status_file}" 2>/dev/null)" else DEPLOY_OUTCOME=unknown fi if [ -z "${TELEGRAM_BOT_TOKEN:-}" ] || [ -z "${TELEGRAM_CHAT_ID:-}" ]; then echo "Telegram deployment report skipped: TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID is not configured." exit 0 fi case "${DEPLOY_OUTCOME:-unknown}" in success) heading="✅ Деплой production успешно завершён" production_state="Ревизия ${DEPLOY_SHA} активна в production." ;; failure) heading="❌ Деплой production завершился ошибкой" production_state="Успешный запуск новой ревизии не подтверждён. Проверьте журнал деплоя и результат отката." ;; cancelled) heading="⚠️ Деплой production был отменён" production_state="Запрошенная ревизия не подтверждена как активная." ;; *) heading="⚠️ Деплой production завершён со статусом ${DEPLOY_OUTCOME:-unknown}" production_state="Запрошенная ревизия не подтверждена как активная." ;; esac # Gitea 1.23 uses the repository run index (run_number) in web URLs, # not the internal database ID (run_id). Keep run_id for status files. run_url="${GITEA_SERVER_URL%/}/${DEPLOY_REPOSITORY}/actions/runs/${GITEA_RUN_NUMBER}" message="$(printf '%s\n\nРепозиторий: %s\nВетка: %s\nРевизия: %s\nИнициатор: %s\nТекущее состояние: %s\nЗапуск: %s' \ "${heading}" \ "${DEPLOY_REPOSITORY}" \ "${DEPLOY_REF}" \ "${DEPLOY_SHA}" \ "${DEPLOY_ACTOR}" \ "${production_state}" \ "${run_url}")" response_file="$(mktemp)" telegram_url="https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" report_sent=false if command -v curl >/dev/null 2>&1; then http_code="$(curl \ --silent \ --output "${response_file}" \ --write-out '%{http_code}' \ --connect-timeout 5 \ --max-time 15 \ --retry 2 \ --retry-delay 2 \ --request POST \ "${telegram_url}" \ --data-urlencode "chat_id=${TELEGRAM_CHAT_ID}" \ --data-urlencode "text=${message}" \ --data-urlencode "disable_web_page_preview=true" 2>/dev/null)" transport_status=$? if [ "${transport_status}" -eq 0 ] \ && [ "${http_code}" -ge 200 ] 2>/dev/null \ && [ "${http_code}" -lt 300 ] 2>/dev/null; then report_sent=true else echo "Telegram report through curl failed; trying another available transport." fi fi if [ "${report_sent}" != true ] && command -v wget >/dev/null 2>&1; then compact_message="$(printf '%s | Репозиторий: %s | Ветка: %s | Ревизия: %s | Инициатор: %s | Текущее состояние: %s | Запуск: %s' \ "${heading}" \ "${DEPLOY_REPOSITORY}" \ "${DEPLOY_REF}" \ "${DEPLOY_SHA}" \ "${DEPLOY_ACTOR}" \ "${production_state}" \ "${run_url}")" # This fallback must work without Python. JSON-escape user-facing # metadata so quotes/backslashes in an actor name cannot break it. json_escape() { printf '%s' "$1" | tr '\r\n\t' ' ' | sed 's/\\/\\\\/g; s/"/\\"/g' } telegram_payload="$(printf '{"chat_id":"%s","text":"%s","disable_web_page_preview":true}' \ "$(json_escape "${TELEGRAM_CHAT_ID}")" "$(json_escape "${compact_message}")")" wget \ -q \ -O "${response_file}" \ -T 15 \ -t 3 \ --header='Content-Type: application/json' \ --post-data="${telegram_payload}" \ "${telegram_url}" 2>/dev/null transport_status=$? if [ "${transport_status}" -eq 0 ]; then report_sent=true else echo "Telegram report through wget failed; trying another available transport." fi fi if [ "${report_sent}" != true ] && command -v python3 >/dev/null 2>&1; then TELEGRAM_MESSAGE="${message}" python3 - <<'PY' import json import os import urllib.request payload = json.dumps( { "chat_id": os.environ["TELEGRAM_CHAT_ID"], "text": os.environ["TELEGRAM_MESSAGE"], "disable_web_page_preview": True, } ).encode() request = urllib.request.Request( f"https://api.telegram.org/bot{os.environ['TELEGRAM_BOT_TOKEN']}/sendMessage", data=payload, headers={"Content-Type": "application/json"}, method="POST", ) try: with urllib.request.urlopen(request, timeout=15) as response: if not 200 <= response.status < 300: raise RuntimeError("Non-successful Telegram response") except Exception as exc: # Never print the token-bearing request URL or an HTTP body. print(f"Telegram Python transport failed ({type(exc).__name__}).") raise SystemExit(1) from None PY transport_status=$? if [ "${transport_status}" -eq 0 ]; then report_sent=true else echo "Telegram report through Python failed." fi fi rm -f "${response_file}" if [ "${report_sent}" = true ]; then echo "Telegram deployment report sent." else echo "Telegram deployment report skipped: no available transport could deliver it." fi exit 0