feat(ops): бэкапить конфигурацию Forgejo, а не только его содержимое
All checks were successful
CI / changes (pull_request) Successful in 9s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped

backup-forgejo.sh забирал БД и голые репозитории — то есть СОДЕРЖИМОЕ
Forgejo, но не то, ЧЕМ оно поднимается.

Компоуз-файлы Forgejo и раннеров живут в /home/gendesign/forgejo/, ВНЕ
репозитория: `git rev-parse` там отвечает "not a git repository". Их не
трогает ни один деплой (включая #3071 — тот синхронизирует /opt/gendesign)
и не покрывал ни один бэкап.

Практический смысл: при потере хоста БД и репозитории восстановятся, а
конфигурация — нет. Пришлось бы вручную воссоздавать docker-compose.yml и
заново регистрировать три раннера. Для переезда это особенно неприятно:
Forgejo ОСТАЁТСЯ на Beget, то есть он и есть та машина, с которой всё
будет подниматься, если что-то пойдёт не так.

Косвенное подтверждение, что каталог правится руками и без истории:
рядом лежат runner-compose.yml.bak, .bak.1779049805, .bak.before-runner3 —
три самодельных снимка вместо version control.

Что НЕ включено намеренно: секрет-файл режима 600 и runner*/data/ с
регистрационными токенами. Они остаются ручным off-box пунктом владельца —
тем же, что рантайм-секреты в приёмке #2203. Раннеры при восстановлении
перерегистрируются из UI за пару минут; компоуз-файл руками не
восстановишь.

Проверки те же две, что у repo-бандла (не «объём», а «собрался и
читается»), плюс мягкая деградация: не собрался — WARN и продолжаем без
него, бэкап БД и репозиториев не теряем из-за 5 КБ конфига.

Проверено на настоящем каталоге прода (read-only, боевой бэкап не
тронут):
  - tar код 0, ровно 2 файла: docker-compose.yml, runner-compose.yml
  - 1070 байт, архив читается
  - секрет-файла внутри нет, каталога data/ внутри нет
  - раскрытие ${config_out:+...} в цикле выгрузки: пусто -> 2 файла,
    задано -> 3
  - bash -n чист

Refs #2203, #3057
This commit is contained in:
bot-backend 2026-08-24 06:47:27 +03:00
parent 0ba52e55db
commit 03a600db65

View file

@ -166,8 +166,56 @@ fi
log "Repo bundle OK: ${repos_out} ($(du -h "$repos_out" | cut -f1), ${repos_bytes} bytes)"
# --- 3. upload to S3 (mandatory — see guard 0 above) ---
for f in "$db_out" "$repos_out"; do
# --- 3. config bundle (#2203 follow-up) ---
# ПОЧЕМУ ЭТО ОТДЕЛЬНЫЙ АРТЕФАКТ. Бэкап забирал БД и голые репозитории — то есть
# СОДЕРЖИМОЕ Forgejo, но не то, ЧЕМ оно поднимается. Компоуз-файлы Forgejo и
# раннеров живут в /home/gendesign/forgejo/, вне репозитория (проверено:
# `git rev-parse` там отвечает "not a git repository"), их не трогает ни один
# деплой и не покрывал ни один бэкап.
#
# Практический смысл: при потере хоста БД и репозитории восстанавливаются, а
# КОНФИГУРАЦИЯ — нет. Пришлось бы вручную воссоздавать docker-compose.yml и
# заново регистрировать три раннера. Файлы крошечные (~5 КБ на оба), так что
# цена включения нулевая, а цена отсутствия — ручная реконструкция в худший
# момент.
#
# Косвенное подтверждение, что каталог правится руками и без истории: рядом
# лежат runner-compose.yml.bak, .bak.1779049805, .bak.before-runner3 — три
# самодельных снимка вместо version control.
#
# ЧТО НАМЕРЕННО НЕ ВКЛЮЧЕНО:
# .env (режим 600) и runner*/data/ с регистрационными токенами — секреты.
# Они остаются ручным off-box пунктом владельца, тем же, что .env.runtime
# в приёмке #2203. Раннеры при восстановлении перерегистрируются из UI за
# пару минут; компоуз-файл руками не восстановишь.
config_out="${LOCAL_BACKUP_DIR}/forgejo-config_${ts}.tar.gz"
log "Bundling Forgejo compose config from ${FORGEJO_DIR} -> ${config_out}"
# -C + явные имена, а не путь целиком: в архиве оказываются только те файлы,
# что перечислены, без каталога data/ (он огромен и уже покрыт repo-бандлом).
if ! tar -czf "$config_out" -C "$FORGEJO_DIR" \
--exclude='.env' \
$(cd "$FORGEJO_DIR" && ls *.yml 2>/dev/null) 2>/dev/null; then
log "WARN: не удалось собрать config-бандл — продолжаю без него" >&2
rm -f "$config_out"
config_out=""
fi
if [[ -n "$config_out" ]]; then
# Порог намеренно низкий: файлы и правда маленькие. Проверяем не «объём», а
# «архив собрался и читается» — те же две проверки, что у repo-бандла.
config_bytes=$(wc -c < "$config_out" | tr -d ' ')
if (( config_bytes < 200 )) || ! tar -tzf "$config_out" >/dev/null 2>&1; then
log "WARN: config-бандл подозрителен (${config_bytes} байт / не читается) — не выгружаю" >&2
rm -f "$config_out"
config_out=""
else
log "Config bundle OK: ${config_out} (${config_bytes} байт, файлов: $(tar -tzf "$config_out" | wc -l))"
fi
fi
# --- 4. upload to S3 (mandatory — see guard 0 above) ---
for f in "$db_out" "$repos_out" ${config_out:+"$config_out"}; do
key="${FORGEJO_S3_PREFIX%/}/$(basename "$f")"
log "Uploading to s3://${FORGEJO_S3_BUCKET}/${key}"
# Same AWS_CA_BUNDLE fix as ops/backup.sh / backup-tradein-db.sh — aws-cli