From 26f8a81631d152a8ad8c4a1142a52d112fb97e96 Mon Sep 17 00:00:00 2001 From: Ivan Fontosh Date: Wed, 13 May 2026 23:48:39 +0800 Subject: [PATCH] fix(ci): more git push retries for feed; document HTTP 500 and temp branch Co-authored-by: Cursor --- .gitea/workflows/release.yml | 2 ++ docs/GITEA_AUTO_UPDATE.md | 6 +++--- scripts/sync-update-feed.mjs | 29 ++++++++++++++++++++++------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 438c6f3..0c6686c 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -136,6 +136,8 @@ jobs: ARTIFACT_LINUX: ${{ github.workspace }}/release DND_FEED_TMP_ROOT: ${{ github.workspace }}/.dnd-feed-tmp DND_UPDATES_SQUASH_HISTORY: '1' + DND_GIT_PUSH_RETRIES: '8' + DND_GIT_PUSH_RETRY_SLEEP_SEC: '35' GIT_COMMIT_TAG: ${{ github.ref_name }} run: | set -euo pipefail diff --git a/docs/GITEA_AUTO_UPDATE.md b/docs/GITEA_AUTO_UPDATE.md index 4b01bc1..c877f9c 100644 --- a/docs/GITEA_AUTO_UPDATE.md +++ b/docs/GITEA_AUTO_UPDATE.md @@ -362,13 +362,13 @@ git push origin v1.0.1 ## Если `git push` в `updates` падает: `remote end hung up` / таймаут -Один коммит с **Windows + два AppImage** может быть **сотни МБ** — HTTPS-push иногда рвётся из‑за лимита буфера Git или таймаута **nginx / reverse proxy** перед Gitea. +Один коммит с **Windows + два AppImage** может быть **сотни МБ** — HTTPS-push иногда рвётся из‑за лимита буфера Git или таймаута **nginx / reverse proxy** перед Gitea. Отдельно встречается **`HTTP 500`** от Gitea/прокси на большом pack (например один `*-win.zip` ~200 MiB) — это уже **ошибка сервера** при приёме тела запроса; повторы push иногда помогают, но при стабильном 500 нужно править лимиты/таймауты на стороне Gitea и reverse proxy. **В репозитории с кодом** скрипт `scripts/sync-update-feed.mjs` уже выставляет в клоне feed-репо: - `http.postBuffer` **2 GiB**; - отключение «медленной передачи» (`http.lowSpeedLimit` / `http.lowSpeedTime`); -- до **3** повторов `git push` с паузой 20 с (переменная **`DND_GIT_PUSH_RETRIES`**, максимум 5). +- повторы **`git push`**: переменная **`DND_GIT_PUSH_RETRIES`** (по умолчанию **5**, максимум **10**), пауза **`DND_GIT_PUSH_RETRY_SLEEP_SEC`** (по умолчанию **30** с, максимум **120**). В workflow для шага sync задано **`DND_GIT_PUSH_RETRIES=8`** и **`DND_GIT_PUSH_RETRY_SLEEP_SEC=35`**. Если ошибка сохраняется — на **сервере** (nginx и т.п.) проверьте, например: @@ -382,7 +382,7 @@ git push origin v1.0.1 Переписывается **только** публичный репозиторий из секрета **`UPDATES_REPO`**, ветка **`updates`**. Репозиторий с исходниками игры (**DndGamePlayer**) и его теги **не меняются**. -В workflow задано **`DND_UPDATES_SQUASH_HISTORY=1`**: после merge и подкладки артефактов скрипт делает **`git checkout --orphan`** и собирает историю **только текущего релиза**. Чтобы не отправлять один огромный pack (~700+ MiB) и не ловить `curl 55 Broken pipe`, загрузка идёт через временную ветку **`updates-upload-*`**: сначала small files, затем каждый большой файл отдельным push (порог **`DND_FEED_LARGE_FILE_BYTES`**, по умолчанию 64 MiB). Ветка **`updates`** передвигается на готовый финальный коммит только в конце через **`--force-with-lease`** (если ветка уже была) или обычный первый push. Пользователи не видят «полурелиз», потому что `updates` меняется только после полной загрузки. +В workflow задано **`DND_UPDATES_SQUASH_HISTORY=1`**: после merge и подкладки артефактов скрипт делает **`git checkout --orphan`** и собирает историю **только текущего релиза**. Чтобы не отправлять один огромный pack (~700+ MiB) и не ловить `curl 55 Broken pipe`, загрузка идёт через **временную** ветку **`updates-upload-*`**: сначала small files, затем каждый большой файл отдельным push (порог **`DND_FEED_LARGE_FILE_BYTES`**, по умолчанию 64 MiB). Эта ветка **не заменяет** публичный feed: URL для клиентов по-прежнему **`…/raw/branch/updates/`**. `updates-upload-*` — только черновик на время job; в конце ветка **`updates`** передвигается на готовый коммит (`--force-with-lease` или первый push), временная ветка **удаляется** с remote. На **сервере Gitea** старые объекты коммитов остаются «висячими», пока не отработает **сборка мусора** репозитория (настройки сервера / ручной `git gc` в bare-репо). Для пользователей приложения важны только URL **`latest*.yml`** и установщиков — они не меняются по смыслу. diff --git a/scripts/sync-update-feed.mjs b/scripts/sync-update-feed.mjs index 7ba754f..48dc5a4 100644 --- a/scripts/sync-update-feed.mjs +++ b/scripts/sync-update-feed.mjs @@ -13,12 +13,13 @@ * ARTIFACT_MAC — каталог с файлами macOS * ARTIFACT_LINUX — каталог с файлами Linux (AppImage и т.д.) * GIT_COMMIT_TAG — опционально, для сообщения коммита - * DND_GIT_PUSH_RETRIES — опционально, число попыток git push (1–5, по умолчанию 3) + * DND_GIT_PUSH_RETRIES — число попыток git push (1–10, по умолчанию 5); при squash — для каждого push во временную ветку и финального push в updates + * DND_GIT_PUSH_RETRY_SLEEP_SEC — пауза между попытками push (5–120 с, по умолчанию 30) * DND_UPDATES_CLONE_DEPTH — опционально, глубина shallow clone (2–200, по умолчанию 40), чтобы merge с remote был надёжнее * DND_FEED_TMP_ROOT — каталог для временного клона feed (по умолчанию GITHUB_WORKSPACE / TMPDIR / os.tmpdir); не используйте узкий /tmp на раннере * DND_GIT_FETCH_RETRIES — число попыток git fetch (1–6, по умолчанию 6); между попытками — git gc --prune=now (освобождение после обрыва TLS/unpack) * DND_FEED_SKIP_DISK_CHECK — если "1", не проверять свободное место на томе DND_FEED_TMP_ROOT перед clone - * DND_UPDATES_SQUASH_HISTORY — если "1", после каждого релиза ветка updates в UPDATES_REPO переписывается на историю только текущего релиза (orphan + временная ветка + force-with-lease) + * DND_UPDATES_SQUASH_HISTORY — если "1", после каждого релиза ветка updates в UPDATES_REPO переписывается на историю только текущего релиза. Загрузка идёт во временную ветку updates-upload-* (маленькие pack'и), затем публикуется updates; временная ветка удаляется — пользователям feed по-прежнему нужна только ветка updates. * DND_FEED_LARGE_FILE_BYTES — порог "большого" файла для дробления push в squash-режиме (по умолчанию 64 MiB) */ import { execFileSync, spawnSync } from 'node:child_process'; @@ -200,8 +201,20 @@ function sanitizeRefPart(s) { .slice(0, 80); } +function gitPushRetryCount() { + return Math.max(1, Math.min(10, Number.parseInt(process.env.DND_GIT_PUSH_RETRIES || '5', 10) || 5)); +} + +function gitPushRetrySleepSec() { + return Math.max( + 5, + Math.min(120, Number.parseInt(process.env.DND_GIT_PUSH_RETRY_SLEEP_SEC || '30', 10) || 30), + ); +} + function pushWithRetries(work, args, label) { - const retries = Math.max(1, Math.min(5, Number.parseInt(process.env.DND_GIT_PUSH_RETRIES || '3', 10) || 3)); + const retries = gitPushRetryCount(); + const sleepSec = gitPushRetrySleepSec(); let lastError; for (let attempt = 1; attempt <= retries; attempt += 1) { try { @@ -212,7 +225,7 @@ function pushWithRetries(work, args, label) { lastError = err; console.warn(`[sync-update-feed] ${label} failed (attempt ${attempt}/${retries})`); if (attempt < retries) { - sleepSyncSeconds(20); + sleepSyncSeconds(sleepSec); } } } @@ -269,7 +282,8 @@ function publishSquashedUpdatesBranchStreamed(work, message, tag) { console.warn( `[sync-update-feed] DND_UPDATES_SQUASH_HISTORY=1: переписываем только UPDATES_REPO/updates; ` + - `push дробится через временную ветку ${tempBranch} (${smallFiles.length} small, ${largeFiles.length} large)`, + `push дробится через временную ветку ${tempBranch} (${smallFiles.length} small, ${largeFiles.length} large). ` + + `Ветка updates-upload-* служебная: после успеха на remote остаётся только updates; клиенты по-прежнему читают raw/.../updates/.`, ); runGit(['checkout', '--orphan', 'dnd-feed-upload-tmp'], work); @@ -354,7 +368,8 @@ function mergeOriginUpdates(work) { } function pushUpdatesBranch(work) { - const retries = Math.max(1, Math.min(5, Number.parseInt(process.env.DND_GIT_PUSH_RETRIES || '3', 10) || 3)); + const retries = gitPushRetryCount(); + const sleepSec = gitPushRetrySleepSec(); let lastError; for (let attempt = 1; attempt <= retries; attempt += 1) { try { @@ -366,7 +381,7 @@ function pushUpdatesBranch(work) { lastError = err; console.warn(`[sync-update-feed] merge/push failed (attempt ${attempt}/${retries})`); if (attempt < retries) { - sleepSyncSeconds(20); + sleepSyncSeconds(sleepSec); } } }