From 4f5d61f0e82235178d0e1a72f77ceb9d6ea47996 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Thu, 13 Aug 2026 17:41:32 +0500 Subject: [PATCH 1/3] =?UTF-8?q?ci:=20=D1=83=D0=B1=D0=B8=D1=80=D0=B0=D1=82?= =?UTF-8?q?=D1=8C=20buildx-=D0=B1=D0=B8=D0=BB=D0=B4=D0=B5=D1=80=20=D0=B2?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BD=D1=86=D0=B5=20=D1=81=D0=B1=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D1=87=D0=BD=D1=8B=D1=85=20job'=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон, а его post-step под Forgejo act_runner не срабатывает. К 13.08 на хосте накопилось 20 контейнеров buildx_buildkit_builder-* возрастом до двух месяцев, ~19 ГБ в их `_state`-томах: диск ушёл на 94%, деплой упал с `no space left on device`, и тем же дефицитом покрасились ещё две проверки на других PR. Тома не dangling (контейнеры существуют), поэтому `docker volume prune` их не видит; `docker system df` показывает `Build Cache 0B`, потому что кэш живёт внутри контейнеров, а не у хостового демона — рост незаметен до отказа. Шесть сборочных job'ов (три в deploy.yml, три в deploy-tradein.yml) получают `id: buildx` и шаг уборки в конце. `if: always()` — чтобы убирал и после падения сборки; ошибка уборки не роняет прогон. Шаг ГОВОРИТ, когда не сработал: если `steps.buildx.outputs.name` пуст (а под act это возможно), в лог уходит явная строка, а не молчаливый no-op. Refs #2869 --- .forgejo/workflows/deploy-tradein.yml | 54 +++++++++++++++++++++++++++ .forgejo/workflows/deploy.yml | 54 +++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/.forgejo/workflows/deploy-tradein.yml b/.forgejo/workflows/deploy-tradein.yml index d45dd831..b9cc41bc 100644 --- a/.forgejo/workflows/deploy-tradein.yml +++ b/.forgejo/workflows/deploy-tradein.yml @@ -229,6 +229,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + id: buildx - name: Build & push tradein-backend uses: docker/build-push-action@v6 @@ -251,6 +252,23 @@ jobs: ${{ env.IMAGE_BACKEND }}:latest ${{ env.IMAGE_BACKEND }}:${{ github.sha }} + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) + # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. + # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на + # хосте накопилось 20 контейнеров возрастом до двух месяцев и ~19 ГБ в + # их `_state`-томах — диск ушёл на 94%, деплой упал с + # `no space left on device`. Убираем явно, `if: always()` и `|| true`, + # чтобы уборка не могла уронить прогон. + if: always() + run: | + name="${{ steps.buildx.outputs.name }}" + if [ -z "$name" ]; then + echo "buildx: имя билдера не пришло из outputs — уборка НЕ сработала (см. #2869)" + exit 0 + fi + echo "buildx: убираю билдер $name" + docker buildx rm --force "$name" || echo "buildx: не удалось убрать $name (не фатально)" + build-frontend: runs-on: ubuntu-latest needs: changes @@ -269,6 +287,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + id: buildx # CHANGELOG.md живёт в tradein-mvp/, ОДИН уровень выше build context # (./tradein-mvp/frontend) — Docker не пускает COPY за пределы контекста, @@ -303,6 +322,23 @@ jobs: ${{ env.IMAGE_FRONTEND }}:latest ${{ env.IMAGE_FRONTEND }}:${{ github.sha }} + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) + # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. + # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на + # хосте накопилось 20 контейнеров возрастом до двух месяцев и ~19 ГБ в + # их `_state`-томах — диск ушёл на 94%, деплой упал с + # `no space left on device`. Убираем явно, `if: always()` и `|| true`, + # чтобы уборка не могла уронить прогон. + if: always() + run: | + name="${{ steps.buildx.outputs.name }}" + if [ -z "$name" ]; then + echo "buildx: имя билдера не пришло из outputs — уборка НЕ сработала (см. #2869)" + exit 0 + fi + echo "buildx: убираю билдер $name" + docker buildx rm --force "$name" || echo "buildx: не удалось убрать $name (не фатально)" + build-browser: runs-on: ubuntu-latest needs: changes @@ -323,6 +359,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + id: buildx - name: Build & push tradein-browser uses: docker/build-push-action@v6 @@ -335,6 +372,23 @@ jobs: ${{ env.IMAGE_BROWSER }}:latest ${{ env.IMAGE_BROWSER }}:${{ github.sha }} + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) + # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. + # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на + # хосте накопилось 20 контейнеров возрастом до двух месяцев и ~19 ГБ в + # их `_state`-томах — диск ушёл на 94%, деплой упал с + # `no space left on device`. Убираем явно, `if: always()` и `|| true`, + # чтобы уборка не могла уронить прогон. + if: always() + run: | + name="${{ steps.buildx.outputs.name }}" + if [ -z "$name" ]; then + echo "buildx: имя билдера не пришло из outputs — уборка НЕ сработала (см. #2869)" + exit 0 + fi + echo "buildx: убираю билдер $name" + docker buildx rm --force "$name" || echo "buildx: не удалось убрать $name (не фатально)" + deploy: runs-on: ubuntu-latest needs: [changes, test, build-backend, build-frontend, build-browser] diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index f267444a..9a9af055 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -73,6 +73,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + id: buildx - name: Build & push backend (lean — без Chromium) uses: docker/build-push-action@v6 @@ -86,6 +87,23 @@ jobs: ${{ env.IMAGE_BACKEND }}:latest ${{ env.IMAGE_BACKEND }}:${{ github.sha }} + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) + # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. + # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на + # хосте накопилось 20 контейнеров возрастом до двух месяцев и ~19 ГБ в + # их `_state`-томах — диск ушёл на 94%, деплой упал с + # `no space left on device`. Убираем явно, `if: always()` и `|| true`, + # чтобы уборка не могла уронить прогон. + if: always() + run: | + name="${{ steps.buildx.outputs.name }}" + if [ -z "$name" ]; then + echo "buildx: имя билдера не пришло из outputs — уборка НЕ сработала (см. #2869)" + exit 0 + fi + echo "buildx: убираю билдер $name" + docker buildx rm --force "$name" || echo "buildx: не удалось убрать $name (не фатально)" + build-worker: runs-on: ubuntu-latest needs: changes @@ -104,6 +122,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + id: buildx - name: Build & push worker (с Chromium для Playwright) uses: docker/build-push-action@v6 @@ -117,6 +136,23 @@ jobs: ${{ env.IMAGE_WORKER }}:latest ${{ env.IMAGE_WORKER }}:${{ github.sha }} + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) + # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. + # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на + # хосте накопилось 20 контейнеров возрастом до двух месяцев и ~19 ГБ в + # их `_state`-томах — диск ушёл на 94%, деплой упал с + # `no space left on device`. Убираем явно, `if: always()` и `|| true`, + # чтобы уборка не могла уронить прогон. + if: always() + run: | + name="${{ steps.buildx.outputs.name }}" + if [ -z "$name" ]; then + echo "buildx: имя билдера не пришло из outputs — уборка НЕ сработала (см. #2869)" + exit 0 + fi + echo "buildx: убираю билдер $name" + docker buildx rm --force "$name" || echo "buildx: не удалось убрать $name (не фатально)" + build-frontend: runs-on: ubuntu-latest needs: changes @@ -135,6 +171,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + id: buildx - name: Build & push frontend uses: docker/build-push-action@v6 @@ -150,6 +187,23 @@ jobs: ${{ env.IMAGE_FRONTEND }}:latest ${{ env.IMAGE_FRONTEND }}:${{ github.sha }} + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) + # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. + # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на + # хосте накопилось 20 контейнеров возрастом до двух месяцев и ~19 ГБ в + # их `_state`-томах — диск ушёл на 94%, деплой упал с + # `no space left on device`. Убираем явно, `if: always()` и `|| true`, + # чтобы уборка не могла уронить прогон. + if: always() + run: | + name="${{ steps.buildx.outputs.name }}" + if [ -z "$name" ]; then + echo "buildx: имя билдера не пришло из outputs — уборка НЕ сработала (см. #2869)" + exit 0 + fi + echo "buildx: убираю билдер $name" + docker buildx rm --force "$name" || echo "buildx: не удалось убрать $name (не фатально)" + deploy: runs-on: ubuntu-latest needs: [changes, build-backend, build-worker, build-frontend] -- 2.45.3 From 9a43f00a5e4819c2bf1d0a1c56bc1d580cea78a4 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Thu, 13 Aug 2026 17:43:43 +0500 Subject: [PATCH 2/3] =?UTF-8?q?ci:=20=D0=BF=D0=BE=D0=B4=D0=B1=D0=B8=D1=80?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20=D0=BF=D1=80=D0=BE=D1=82=D1=91=D0=BA=D1=88?= =?UTF-8?q?=D0=B8=D0=B5=20buildx-=D0=B1=D0=B8=D0=BB=D0=B4=D0=B5=D1=80?= =?UTF-8?q?=D1=8B=20=D0=BD=D0=B0=20=D0=B2=D1=85=D0=BE=D0=B4=D0=B5=20=D1=81?= =?UTF-8?q?=D0=B1=D0=BE=D1=80=D0=BE=D1=87=D0=BD=D1=8B=D1=85=20job'=D0=BE?= =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Поправка к первому коммиту: там я написал, что post-step у setup-buildx-action «не срабатывает». Замер это опроверг. Даты создания 20 висящих билдеров: 17.05, 30.05, 31.05, 13.06, 17.06, 20.06, 28.06, 05.07 — и НИ ОДНОГО за пять недель между 05.07 и 13.08. Два последних созданы 13.08 в 11:57:43, ровно тем прогоном, что упал с `no space left on device`. То есть штатная уборка работает, а билдеры текут, когда job умирает АВАРИЙНО (ENOSPC, OOM, отмена concurrency-группой) — тогда контейнер job'а мёртв и завершающие шаги не выполняются вообще. Из этого следует, что завершающий шаг из первого коммита сам по себе почти бесполезен: он не отработает ровно в тех случаях, которые и создают мусор. Поэтому добавлен шаг НА ВХОДЕ: подбирает чужие протёкшие билдеры старше 6 часов (самый долгий job — ~17 минут, живой прогон под порог не попадает) вместе с их `_state`-томами и печатает, сколько убрал и сколько места на диске. Завершающий шаг оставлен — он дешёвый и закрывает обычные падения. Refs #2869 --- .forgejo/workflows/deploy-tradein.yml | 84 +++++++++++++++++++++++++++ .forgejo/workflows/deploy.yml | 84 +++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) diff --git a/.forgejo/workflows/deploy-tradein.yml b/.forgejo/workflows/deploy-tradein.yml index b9cc41bc..8223db58 100644 --- a/.forgejo/workflows/deploy-tradein.yml +++ b/.forgejo/workflows/deploy-tradein.yml @@ -227,6 +227,34 @@ jobs: run: | echo "$GHCR_PAT" | docker login ghcr.io -u lekss361 --password-stdin + - name: Подобрать протёкшие buildx-билдеры (#2869) + # Билдеры протекают НЕ на обычном падении, а когда job умирает аварийно + # (ENOSPC, OOM, отмена concurrency-группой): тогда ни post-step действия, + # ни завершающий шаг не выполняются — контейнер job'а уже мёртв. + # Замер 13.08: 20 висящих билдеров, созданных в 8 дат за три месяца + # (17.05, 30.05, 31.05, 13.06, 17.06, 20.06, 28.06, 05.07) — и ни одного + # за пять недель между 05.07 и 13.08, когда аварий не было. Два последних + # созданы 13.08 11:57:43 — ровно тот прогон, что упал с + # `no space left on device`. + # Поэтому чистим ЧУЖОЙ мусор НА ВХОДЕ: всё старше 6 часов заведомо не + # принадлежит живому прогону (самый долгий job — ~17 минут). + run: | + now=$(date +%s); reaped=0; kept=0 + for c in $(docker ps -a --filter "name=^buildx_buildkit_builder-" --format '{{.Names}}'); do + created=$(docker inspect "$c" --format '{{.Created}}' 2>/dev/null) || continue + ts=$(date -d "$created" +%s 2>/dev/null) || continue + age_h=$(( (now - ts) / 3600 )) + if [ "$age_h" -ge 6 ]; then + echo "buildx: убираю протёкший билдер $c (возраст ${age_h} ч)" + docker rm -f "$c" >/dev/null && reaped=$((reaped+1)) + docker volume rm "${c}_state" >/dev/null 2>&1 || true + else + kept=$((kept+1)) + fi + done + echo "buildx: убрано протёкших ${reaped}, оставлено свежих ${kept}" + df -h / | tail -1 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 id: buildx @@ -285,6 +313,34 @@ jobs: run: | echo "$GHCR_PAT" | docker login ghcr.io -u lekss361 --password-stdin + - name: Подобрать протёкшие buildx-билдеры (#2869) + # Билдеры протекают НЕ на обычном падении, а когда job умирает аварийно + # (ENOSPC, OOM, отмена concurrency-группой): тогда ни post-step действия, + # ни завершающий шаг не выполняются — контейнер job'а уже мёртв. + # Замер 13.08: 20 висящих билдеров, созданных в 8 дат за три месяца + # (17.05, 30.05, 31.05, 13.06, 17.06, 20.06, 28.06, 05.07) — и ни одного + # за пять недель между 05.07 и 13.08, когда аварий не было. Два последних + # созданы 13.08 11:57:43 — ровно тот прогон, что упал с + # `no space left on device`. + # Поэтому чистим ЧУЖОЙ мусор НА ВХОДЕ: всё старше 6 часов заведомо не + # принадлежит живому прогону (самый долгий job — ~17 минут). + run: | + now=$(date +%s); reaped=0; kept=0 + for c in $(docker ps -a --filter "name=^buildx_buildkit_builder-" --format '{{.Names}}'); do + created=$(docker inspect "$c" --format '{{.Created}}' 2>/dev/null) || continue + ts=$(date -d "$created" +%s 2>/dev/null) || continue + age_h=$(( (now - ts) / 3600 )) + if [ "$age_h" -ge 6 ]; then + echo "buildx: убираю протёкший билдер $c (возраст ${age_h} ч)" + docker rm -f "$c" >/dev/null && reaped=$((reaped+1)) + docker volume rm "${c}_state" >/dev/null 2>&1 || true + else + kept=$((kept+1)) + fi + done + echo "buildx: убрано протёкших ${reaped}, оставлено свежих ${kept}" + df -h / | tail -1 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 id: buildx @@ -357,6 +413,34 @@ jobs: run: | echo "$GHCR_PAT" | docker login ghcr.io -u lekss361 --password-stdin + - name: Подобрать протёкшие buildx-билдеры (#2869) + # Билдеры протекают НЕ на обычном падении, а когда job умирает аварийно + # (ENOSPC, OOM, отмена concurrency-группой): тогда ни post-step действия, + # ни завершающий шаг не выполняются — контейнер job'а уже мёртв. + # Замер 13.08: 20 висящих билдеров, созданных в 8 дат за три месяца + # (17.05, 30.05, 31.05, 13.06, 17.06, 20.06, 28.06, 05.07) — и ни одного + # за пять недель между 05.07 и 13.08, когда аварий не было. Два последних + # созданы 13.08 11:57:43 — ровно тот прогон, что упал с + # `no space left on device`. + # Поэтому чистим ЧУЖОЙ мусор НА ВХОДЕ: всё старше 6 часов заведомо не + # принадлежит живому прогону (самый долгий job — ~17 минут). + run: | + now=$(date +%s); reaped=0; kept=0 + for c in $(docker ps -a --filter "name=^buildx_buildkit_builder-" --format '{{.Names}}'); do + created=$(docker inspect "$c" --format '{{.Created}}' 2>/dev/null) || continue + ts=$(date -d "$created" +%s 2>/dev/null) || continue + age_h=$(( (now - ts) / 3600 )) + if [ "$age_h" -ge 6 ]; then + echo "buildx: убираю протёкший билдер $c (возраст ${age_h} ч)" + docker rm -f "$c" >/dev/null && reaped=$((reaped+1)) + docker volume rm "${c}_state" >/dev/null 2>&1 || true + else + kept=$((kept+1)) + fi + done + echo "buildx: убрано протёкших ${reaped}, оставлено свежих ${kept}" + df -h / | tail -1 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 id: buildx diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index 9a9af055..ef74594b 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -71,6 +71,34 @@ jobs: run: | echo "$GHCR_PAT" | docker login ghcr.io -u lekss361 --password-stdin + - name: Подобрать протёкшие buildx-билдеры (#2869) + # Билдеры протекают НЕ на обычном падении, а когда job умирает аварийно + # (ENOSPC, OOM, отмена concurrency-группой): тогда ни post-step действия, + # ни завершающий шаг не выполняются — контейнер job'а уже мёртв. + # Замер 13.08: 20 висящих билдеров, созданных в 8 дат за три месяца + # (17.05, 30.05, 31.05, 13.06, 17.06, 20.06, 28.06, 05.07) — и ни одного + # за пять недель между 05.07 и 13.08, когда аварий не было. Два последних + # созданы 13.08 11:57:43 — ровно тот прогон, что упал с + # `no space left on device`. + # Поэтому чистим ЧУЖОЙ мусор НА ВХОДЕ: всё старше 6 часов заведомо не + # принадлежит живому прогону (самый долгий job — ~17 минут). + run: | + now=$(date +%s); reaped=0; kept=0 + for c in $(docker ps -a --filter "name=^buildx_buildkit_builder-" --format '{{.Names}}'); do + created=$(docker inspect "$c" --format '{{.Created}}' 2>/dev/null) || continue + ts=$(date -d "$created" +%s 2>/dev/null) || continue + age_h=$(( (now - ts) / 3600 )) + if [ "$age_h" -ge 6 ]; then + echo "buildx: убираю протёкший билдер $c (возраст ${age_h} ч)" + docker rm -f "$c" >/dev/null && reaped=$((reaped+1)) + docker volume rm "${c}_state" >/dev/null 2>&1 || true + else + kept=$((kept+1)) + fi + done + echo "buildx: убрано протёкших ${reaped}, оставлено свежих ${kept}" + df -h / | tail -1 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 id: buildx @@ -120,6 +148,34 @@ jobs: run: | echo "$GHCR_PAT" | docker login ghcr.io -u lekss361 --password-stdin + - name: Подобрать протёкшие buildx-билдеры (#2869) + # Билдеры протекают НЕ на обычном падении, а когда job умирает аварийно + # (ENOSPC, OOM, отмена concurrency-группой): тогда ни post-step действия, + # ни завершающий шаг не выполняются — контейнер job'а уже мёртв. + # Замер 13.08: 20 висящих билдеров, созданных в 8 дат за три месяца + # (17.05, 30.05, 31.05, 13.06, 17.06, 20.06, 28.06, 05.07) — и ни одного + # за пять недель между 05.07 и 13.08, когда аварий не было. Два последних + # созданы 13.08 11:57:43 — ровно тот прогон, что упал с + # `no space left on device`. + # Поэтому чистим ЧУЖОЙ мусор НА ВХОДЕ: всё старше 6 часов заведомо не + # принадлежит живому прогону (самый долгий job — ~17 минут). + run: | + now=$(date +%s); reaped=0; kept=0 + for c in $(docker ps -a --filter "name=^buildx_buildkit_builder-" --format '{{.Names}}'); do + created=$(docker inspect "$c" --format '{{.Created}}' 2>/dev/null) || continue + ts=$(date -d "$created" +%s 2>/dev/null) || continue + age_h=$(( (now - ts) / 3600 )) + if [ "$age_h" -ge 6 ]; then + echo "buildx: убираю протёкший билдер $c (возраст ${age_h} ч)" + docker rm -f "$c" >/dev/null && reaped=$((reaped+1)) + docker volume rm "${c}_state" >/dev/null 2>&1 || true + else + kept=$((kept+1)) + fi + done + echo "buildx: убрано протёкших ${reaped}, оставлено свежих ${kept}" + df -h / | tail -1 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 id: buildx @@ -169,6 +225,34 @@ jobs: run: | echo "$GHCR_PAT" | docker login ghcr.io -u lekss361 --password-stdin + - name: Подобрать протёкшие buildx-билдеры (#2869) + # Билдеры протекают НЕ на обычном падении, а когда job умирает аварийно + # (ENOSPC, OOM, отмена concurrency-группой): тогда ни post-step действия, + # ни завершающий шаг не выполняются — контейнер job'а уже мёртв. + # Замер 13.08: 20 висящих билдеров, созданных в 8 дат за три месяца + # (17.05, 30.05, 31.05, 13.06, 17.06, 20.06, 28.06, 05.07) — и ни одного + # за пять недель между 05.07 и 13.08, когда аварий не было. Два последних + # созданы 13.08 11:57:43 — ровно тот прогон, что упал с + # `no space left on device`. + # Поэтому чистим ЧУЖОЙ мусор НА ВХОДЕ: всё старше 6 часов заведомо не + # принадлежит живому прогону (самый долгий job — ~17 минут). + run: | + now=$(date +%s); reaped=0; kept=0 + for c in $(docker ps -a --filter "name=^buildx_buildkit_builder-" --format '{{.Names}}'); do + created=$(docker inspect "$c" --format '{{.Created}}' 2>/dev/null) || continue + ts=$(date -d "$created" +%s 2>/dev/null) || continue + age_h=$(( (now - ts) / 3600 )) + if [ "$age_h" -ge 6 ]; then + echo "buildx: убираю протёкший билдер $c (возраст ${age_h} ч)" + docker rm -f "$c" >/dev/null && reaped=$((reaped+1)) + docker volume rm "${c}_state" >/dev/null 2>&1 || true + else + kept=$((kept+1)) + fi + done + echo "buildx: убрано протёкших ${reaped}, оставлено свежих ${kept}" + df -h / | tail -1 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 id: buildx -- 2.45.3 From b58adbbe3ddb6c72c5e29b5a78508428970c8e1d Mon Sep 17 00:00:00 2001 From: bot-backend Date: Thu, 13 Aug 2026 17:45:34 +0500 Subject: [PATCH 3/3] =?UTF-8?q?ci:=20=D0=BF=D0=BE=D0=B4=D0=B1=D0=BE=D1=80?= =?UTF-8?q?=20=D0=B1=D0=B8=D0=BB=D0=B4=D0=B5=D1=80=D0=BE=D0=B2=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D0=B4=D0=BE=D0=BB=D0=B6=D0=B5=D0=BD=20=D0=BF=D0=B0?= =?UTF-8?q?=D0=B4=D0=B0=D1=82=D1=8C=20=D0=BF=D0=BE=D0=B4=20`bash=20-e`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `docker rm -f ... && reaped=$((reaped+1))` отдельным оператором: при неудаче удаления вся конструкция возвращает ненулевой код, и под `bash -e` (режим `run:` в Actions) это уронило бы шаг — то есть уборка мусора могла бы уронить деплой. Переписано на if/else с явным сообщением в лог. Логика порога проверена на РЕАЛЬНЫХ датах с прода (20 контейнеров): убрало бы 18, оставило 2 (сегодняшние, возрастом ~1ч). Refs #2869 --- .forgejo/workflows/deploy-tradein.yml | 18 +++++++++++++++--- .forgejo/workflows/deploy.yml | 18 +++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/.forgejo/workflows/deploy-tradein.yml b/.forgejo/workflows/deploy-tradein.yml index 8223db58..50311309 100644 --- a/.forgejo/workflows/deploy-tradein.yml +++ b/.forgejo/workflows/deploy-tradein.yml @@ -246,7 +246,11 @@ jobs: age_h=$(( (now - ts) / 3600 )) if [ "$age_h" -ge 6 ]; then echo "buildx: убираю протёкший билдер $c (возраст ${age_h} ч)" - docker rm -f "$c" >/dev/null && reaped=$((reaped+1)) + if docker rm -f "$c" >/dev/null 2>&1; then + reaped=$((reaped+1)) + else + echo "buildx: не удалось убрать $c (не фатально)" + fi docker volume rm "${c}_state" >/dev/null 2>&1 || true else kept=$((kept+1)) @@ -332,7 +336,11 @@ jobs: age_h=$(( (now - ts) / 3600 )) if [ "$age_h" -ge 6 ]; then echo "buildx: убираю протёкший билдер $c (возраст ${age_h} ч)" - docker rm -f "$c" >/dev/null && reaped=$((reaped+1)) + if docker rm -f "$c" >/dev/null 2>&1; then + reaped=$((reaped+1)) + else + echo "buildx: не удалось убрать $c (не фатально)" + fi docker volume rm "${c}_state" >/dev/null 2>&1 || true else kept=$((kept+1)) @@ -432,7 +440,11 @@ jobs: age_h=$(( (now - ts) / 3600 )) if [ "$age_h" -ge 6 ]; then echo "buildx: убираю протёкший билдер $c (возраст ${age_h} ч)" - docker rm -f "$c" >/dev/null && reaped=$((reaped+1)) + if docker rm -f "$c" >/dev/null 2>&1; then + reaped=$((reaped+1)) + else + echo "buildx: не удалось убрать $c (не фатально)" + fi docker volume rm "${c}_state" >/dev/null 2>&1 || true else kept=$((kept+1)) diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index ef74594b..ebdcc75f 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -90,7 +90,11 @@ jobs: age_h=$(( (now - ts) / 3600 )) if [ "$age_h" -ge 6 ]; then echo "buildx: убираю протёкший билдер $c (возраст ${age_h} ч)" - docker rm -f "$c" >/dev/null && reaped=$((reaped+1)) + if docker rm -f "$c" >/dev/null 2>&1; then + reaped=$((reaped+1)) + else + echo "buildx: не удалось убрать $c (не фатально)" + fi docker volume rm "${c}_state" >/dev/null 2>&1 || true else kept=$((kept+1)) @@ -167,7 +171,11 @@ jobs: age_h=$(( (now - ts) / 3600 )) if [ "$age_h" -ge 6 ]; then echo "buildx: убираю протёкший билдер $c (возраст ${age_h} ч)" - docker rm -f "$c" >/dev/null && reaped=$((reaped+1)) + if docker rm -f "$c" >/dev/null 2>&1; then + reaped=$((reaped+1)) + else + echo "buildx: не удалось убрать $c (не фатально)" + fi docker volume rm "${c}_state" >/dev/null 2>&1 || true else kept=$((kept+1)) @@ -244,7 +252,11 @@ jobs: age_h=$(( (now - ts) / 3600 )) if [ "$age_h" -ge 6 ]; then echo "buildx: убираю протёкший билдер $c (возраст ${age_h} ч)" - docker rm -f "$c" >/dev/null && reaped=$((reaped+1)) + if docker rm -f "$c" >/dev/null 2>&1; then + reaped=$((reaped+1)) + else + echo "buildx: не удалось убрать $c (не фатально)" + fi docker volume rm "${c}_state" >/dev/null 2>&1 || true else kept=$((kept+1)) -- 2.45.3