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
70 lines
4.5 KiB
Bash
Executable file
70 lines
4.5 KiB
Bash
Executable file
#!/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
|