gendesign/scripts/check-latest-image-revision.sh
bot-backend f2945b7157
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy Trade-In / changes (push) Successful in 15s
Deploy / deploy-caddy (push) Has been skipped
Deploy Trade-In / build-browser (push) Successful in 57s
Deploy / build-backend (push) Successful in 1m2s
Deploy / build-frontend (push) Successful in 1m8s
Deploy / build-worker (push) Successful in 1m9s
Deploy / deploy (push) Successful in 1m58s
Deploy / deploy-status (push) Successful in 3s
Deploy Trade-In / build-frontend (push) Successful in 3m14s
Deploy / perimeter-smoke (push) Successful in 14s
Deploy Trade-In / test (push) Successful in 4m12s
Deploy Trade-In / build-backend (push) Successful in 33s
Deploy Trade-In / deploy (push) Successful in 2m16s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 9s
fix(ci): деплой падает громко, если :latest отстаёт от головы по компоненту (#2950) (#3023)
2026-08-21 12:19:07 +00:00

70 lines
4.5 KiB
Bash
Executable file
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.

#!/usr/bin/env bash
# Гард свежести образа :latest перед деплоем (#2950).
#
# ЗАЧЕМ. Деплой катит IMAGE_TAG=latest. Forgejo отменяет ещё не стартовавший
# `deploy` предыдущего run'а той же concurrency-группы, когда приходит новый push
# (cancel-in-progress: false объявлен, но на pending-job не действует — 21.08.2026
# 10:35:13 «Has been cancelled» через 4 с после старта следующего run'а). Следующий
# run — например ops-only, с пропущенными билдами — катит :latest как есть. 21.08
# прод получил новый код только потому, что билды предшественника успели за 70 с
# до pull'а; иначе голова main была бы зелёной, а прод — на старом образе.
#
# ЧТО ПРОВЕРЯЕТ. Образ :latest в registry несёт метку org.opencontainers.image.revision
# (ставится build-push-action в deploy.yml / deploy-tradein.yml). Гард требует, чтобы
# эта ревизия СОДЕРЖАЛА последний коммит, трогавший пути компонента (git log по
# checkout'у run'а): то есть :latest не старше того, что обязано было собраться.
# Ревизия новее — нормально (workflow_dispatch собирает всё на голове).
# Пока условие не выполнено — ждёт (билд предшественника ещё пушит) до таймаута,
# затем падает громко: лучше красный деплой, чем тихий откат.
#
# Использование: check-latest-image-revision.sh <image-repo> <timeout_s> -- <path>...
# CHECK_LATEST_SLEEP_S — пауза между опросами (по умолчанию 20; тесты ставят меньше).
# Коды: 0 — свежий; 1 — отстаёт/метки нет/registry не ответил до таймаута; 2 — usage.
set -euo pipefail
IMAGE="${1:-}"; TIMEOUT="${2:-}"
if [ -z "$IMAGE" ] || [ -z "$TIMEOUT" ] || [ "${3:-}" != "--" ] || [ $# -lt 4 ]; then
echo "usage: $0 <image-repo> <timeout_s> -- <path>..." >&2; exit 2
fi
shift 3
SLEEP="${CHECK_LATEST_SLEEP_S:-20}"
LAST="$(git log -1 --format=%H -- "$@" || true)"
if [ -z "$LAST" ]; then
echo "::error::$IMAGE — нет ни одного коммита по путям: $*" >&2; exit 2
fi
read_revision() {
# Печатает ревизию из метки образа или пустую строку. .Image для multi-arch —
# карта по платформам, для single-arch — сам конфиг; разбираем оба вида.
docker buildx imagetools inspect "$IMAGE:latest" --format '{{json .Image}}' 2>/dev/null \
| python3 -c '
import json, sys
try:
d = json.load(sys.stdin)
except Exception:
sys.exit(0)
cands = [d] if isinstance(d, dict) and "config" in d else [v for v in d.values() if isinstance(v, dict)]
for c in cands:
rev = ((c.get("config") or {}).get("Labels") or {}).get("org.opencontainers.image.revision")
if rev:
print(rev); break
' || true
}
deadline=$(( $(date +%s) + TIMEOUT ))
while :; do
REV="$(read_revision)"
if [ -n "$REV" ] && git cat-file -e "${REV}^{commit}" 2>/dev/null \
&& git merge-base --is-ancestor "$LAST" "$REV"; then
echo "$IMAGE:latest — ревизия ${REV:0:10} содержит последний коммит по компоненту ${LAST:0:10}"
exit 0
fi
now=$(date +%s)
if [ "$now" -ge "$deadline" ]; then
echo "::error::$IMAGE:latest несёт ревизию '${REV:-<метки нет / registry не ответил>}', а последний коммит, трогавший [$*] — ${LAST:0:10}. :latest отстаёт (билд предшественника не доехал или отменён) — деплой остановлен, иначе прод получил бы старый образ при зелёной голове (#2950). Если билд того коммита упал — чини его или запусти workflow_dispatch полного деплоя." >&2
exit 1
fi
echo "$IMAGE:latest — ревизия '${REV:-<метки нет>}' ещё не содержит ${LAST:0:10}; жду билд предшественника ($(( deadline - now )) с)"
sleep "$SLEEP"
done