Corp/.gitea/workflows/deploy-production.yml
Игорь Надеин 4f6244ab1f
Some checks failed
Deploy production / deploy (push) Failing after 2s
Fix CORP runtime errors and harden deployment and media access
2026-09-10 16:40:08 +03:00

220 lines
8.9 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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