From 9b3889bb36e1468f56a34b53290d2e277a8ff144 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sat, 15 Aug 2026 17:55:17 +0300 Subject: [PATCH 1/2] =?UTF-8?q?ci(deploy):=20=D1=87=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20=D1=81=D1=82=D0=B0=D1=82=D1=83=D1=81=20?= =?UTF-8?q?=D0=B4=D0=B5=D0=BF=D0=BB=D0=BE=D1=8F=20+=20=D0=BD=D0=B5=D1=84?= =?UTF-8?q?=D0=B0=D1=82=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20buildcache?= =?UTF-8?q?=20(#2841)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Зелёная галка прогона не отличима от пропущенного деплоя: если build падает из-за битого blob в удалённом buildcache, шаг deploy молча пропускается (if-условие даёт result=skipped), а прогон в целом не подсвечен как FAILED. - deploy-status: новая job в конце deploy.yml и deploy-tradein.yml, всегда бежит (if: always() && !cancelled()) и падает явно, если deploy.result != success — неважно, пропущен он (upstream build/test упал) или упал сам. - cache-from нефатален: каждый build-push-action-шаг получил id + continue- on-error, и ретрай без cache-from/cache-to при steps.build.outcome == 'failure'. Битый remote-кеш больше не роняет саму сборку; следующий успешный прогон с кешем перезаписывает buildcache-тег целиком (mode=max) и самолечит порчу. Реальные ошибки сборки (не кеш) по-прежнему валят job на ретрае — deploy-status их тоже поймает. Гейт против публикации services-портов на VPS (та же задача, проблема 1) уже покрыт scripts/check-workflow-ports.py + шагом в ci.yml (#2757/#2759, слит ранее) — сканирует все .forgejo/workflows/*.yml, включая эти два файла; новых правок не потребовалось. docker rm -f БЕЗ -v в SSH-скриптах деплоя не тронут — эти вызовы намеренно без -v (боевые тома), правка их не касается. --- .forgejo/workflows/deploy-tradein.yml | 91 +++++++++++++++++++++++++++ .forgejo/workflows/deploy.yml | 85 +++++++++++++++++++++++++ 2 files changed, 176 insertions(+) diff --git a/.forgejo/workflows/deploy-tradein.yml b/.forgejo/workflows/deploy-tradein.yml index 50311309..8e1ba10a 100644 --- a/.forgejo/workflows/deploy-tradein.yml +++ b/.forgejo/workflows/deploy-tradein.yml @@ -264,6 +264,12 @@ jobs: id: buildx - name: Build & push tradein-backend + # id + continue-on-error: битый blob в удалённом buildcache-манифесте + # валит весь шаг ДО push нового образа — деплой тогда молча + # пропускается (#2841), хотя собрать образ можно и без кеша. Ретрай + # без cache-from — ниже. + id: build + continue-on-error: true uses: docker/build-push-action@v6 with: # Context = tradein-mvp/ (uv workspace root): образу нужен packages/scraper-kit @@ -284,6 +290,23 @@ jobs: ${{ env.IMAGE_BACKEND }}:latest ${{ env.IMAGE_BACKEND }}:${{ github.sha }} + - name: Retry build & push tradein-backend без кеша (битый buildcache, #2841) + # cache-to тоже опущен: следующий успешный прогон С кешем перезапишет + # buildcache-тег целиком (mode=max) и самолечит порчу. + if: steps.build.outcome == 'failure' + uses: docker/build-push-action@v6 + with: + context: ./tradein-mvp + file: ./tradein-mvp/backend/Dockerfile + push: true + build-args: | + APP_VERSION=${{ needs.changes.outputs.app_version }} + BUILD_SHA=${{ needs.changes.outputs.build_sha }} + BUILD_DATE=${{ needs.changes.outputs.build_date }} + tags: | + ${{ env.IMAGE_BACKEND }}:latest + ${{ env.IMAGE_BACKEND }}:${{ github.sha }} + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на @@ -362,6 +385,10 @@ jobs: run: cp tradein-mvp/CHANGELOG.md tradein-mvp/frontend/CHANGELOG.md - name: Build & push tradein-frontend + # id + continue-on-error — см. tradein-backend (#2841): битый blob в + # удалённом buildcache не должен ронять сборку и молча пропускать деплой. + id: build + continue-on-error: true uses: docker/build-push-action@v6 with: context: ./tradein-mvp/frontend @@ -386,6 +413,24 @@ jobs: ${{ env.IMAGE_FRONTEND }}:latest ${{ env.IMAGE_FRONTEND }}:${{ github.sha }} + - name: Retry build & push tradein-frontend без кеша (битый buildcache, #2841) + # См. tradein-backend: cache-to опущен намеренно (следующий успешный + # прогон с кешем перезапишет buildcache-тег целиком и самолечит порчу). + if: steps.build.outcome == 'failure' + uses: docker/build-push-action@v6 + with: + context: ./tradein-mvp/frontend + push: true + build-args: | + NEXT_PUBLIC_BASE_PATH=/trade-in + NEXT_PUBLIC_API_BASE_URL=/trade-in + NEXT_PUBLIC_APP_VERSION=${{ needs.changes.outputs.app_version }} + NEXT_PUBLIC_BUILD_SHA=${{ needs.changes.outputs.build_sha }} + NEXT_PUBLIC_BUILD_DATE=${{ needs.changes.outputs.build_date }} + tags: | + ${{ env.IMAGE_FRONTEND }}:latest + ${{ env.IMAGE_FRONTEND }}:${{ github.sha }} + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на @@ -458,6 +503,10 @@ jobs: id: buildx - name: Build & push tradein-browser + # id + continue-on-error — см. tradein-backend выше (#2841): битый blob + # в удалённом buildcache не должен ронять сборку и молча пропускать деплой. + id: build + continue-on-error: true uses: docker/build-push-action@v6 with: context: ./tradein-mvp/browser @@ -468,6 +517,18 @@ jobs: ${{ env.IMAGE_BROWSER }}:latest ${{ env.IMAGE_BROWSER }}:${{ github.sha }} + - name: Retry build & push tradein-browser без кеша (битый buildcache, #2841) + # См. tradein-backend: cache-to опущен намеренно (следующий успешный + # прогон с кешем перезапишет buildcache-тег целиком и самолечит порчу). + if: steps.build.outcome == 'failure' + uses: docker/build-push-action@v6 + with: + context: ./tradein-mvp/browser + push: true + tags: | + ${{ env.IMAGE_BROWSER }}:latest + ${{ env.IMAGE_BROWSER }}:${{ github.sha }} + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на @@ -1019,3 +1080,33 @@ jobs: # The changes job reads this file on the next run to compute cumulative diff. echo "$GITHUB_SHA" > /opt/gendesign/.tradein-deployed-sha echo "→ Deployed SHA marker updated: $GITHUB_SHA" + + # Честный итог прогона (#2841). ПРОБЛЕМА: `deploy` пропускается своим `if:` + # молча (result=skipped), когда `test` или один из build-* падает (например, + # битый blob в buildcache роняет `docker/build-push-action` — до ретрая + # выше, #2841). skipped-job не красит прогон явным «FAILED» так, чтобы это + # было видно на первый взгляд — итог выглядит зелёным/нейтральным, хотя + # tradein-стек на проде не обновился. Эта job бежит ВСЕГДА (`if: always()`, + # кроме отмены прогона) и сама падает, если deploy не завершился success — + # неважно, пропущен он (test/build упали) или упал сам (SSH/миграция/ + # health-check/сверка образов #2679). Красная точка встаёт именно там, где + # решение реально принято, а не там, где она случайно оказалась по цепочке if. + deploy-status: + runs-on: ubuntu-latest + needs: [test, build-backend, build-frontend, build-browser, deploy] + if: always() && !cancelled() + steps: + - name: Итог прогона — деплой обязан быть success, не skipped/failure + run: | + echo "test: ${{ needs.test.result }}" + echo "build-backend: ${{ needs.build-backend.result }}" + echo "build-frontend: ${{ needs.build-frontend.result }}" + echo "build-browser: ${{ needs.build-browser.result }}" + echo "deploy: ${{ needs.deploy.result }}" + if [ "${{ needs.deploy.result }}" != "success" ]; then + echo "::error::деплой НЕ прошёл (deploy.result=${{ needs.deploy.result }})." \ + "Прогон должен читаться как FAILED, а не как пропущенный шаг (#2841)." \ + "Смотри логи test/build-backend/build-frontend/build-browser/deploy выше." + exit 1 + fi + echo "✓ деплой прошёл успешно" diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index 604e28ee..6c40d79d 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -113,6 +113,12 @@ jobs: id: buildx - name: Build & push backend (lean — без Chromium) + # id + continue-on-error: битый blob в удалённом buildcache-манифесте + # (registry cache, не local) валит весь шаг ДО push нового образа — + # деплой тогда молча пропускается (#2841), хотя код собрать можно, просто + # без кеша. cache-from нефатален: при падении ретраим БЕЗ него ниже. + id: build + continue-on-error: true uses: docker/build-push-action@v6 with: context: ./backend @@ -124,6 +130,21 @@ jobs: ${{ env.IMAGE_BACKEND }}:latest ${{ env.IMAGE_BACKEND }}:${{ github.sha }} + - name: Retry build & push backend без кеша (битый buildcache, #2841) + # cache-to тоже опущен: следующий успешный прогон С кешем перезапишет + # buildcache-тег целиком (mode=max), это самолечит порчу. Если и retry + # упадёт — шаг красный БЕЗ continue-on-error, job честно FAILURE, и + # deploy ниже корректно пропускается (уже настоящая причина, не кеш). + if: steps.build.outcome == 'failure' + uses: docker/build-push-action@v6 + with: + context: ./backend + target: runner + push: true + tags: | + ${{ env.IMAGE_BACKEND }}:latest + ${{ env.IMAGE_BACKEND }}:${{ github.sha }} + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на @@ -194,6 +215,10 @@ jobs: id: buildx - name: Build & push worker (с Chromium для Playwright) + # id + continue-on-error — см. build-backend выше (#2841): битый blob в + # удалённом buildcache не должен ронять сборку и молча пропускать деплой. + id: build + continue-on-error: true uses: docker/build-push-action@v6 with: context: ./backend @@ -205,6 +230,19 @@ jobs: ${{ env.IMAGE_WORKER }}:latest ${{ env.IMAGE_WORKER }}:${{ github.sha }} + - name: Retry build & push worker без кеша (битый buildcache, #2841) + # См. backend: cache-to опущен намеренно (следующий успешный прогон с + # кешем перезапишет buildcache-тег целиком и самолечит порчу). + if: steps.build.outcome == 'failure' + uses: docker/build-push-action@v6 + with: + context: ./backend + target: runner-with-chromium + push: true + tags: | + ${{ env.IMAGE_WORKER }}:latest + ${{ env.IMAGE_WORKER }}:${{ github.sha }} + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на @@ -275,6 +313,10 @@ jobs: id: buildx - name: Build & push frontend + # id + continue-on-error — см. build-backend выше (#2841): битый blob в + # удалённом buildcache не должен ронять сборку и молча пропускать деплой. + id: build + continue-on-error: true uses: docker/build-push-action@v6 with: context: ./frontend @@ -288,6 +330,21 @@ jobs: ${{ env.IMAGE_FRONTEND }}:latest ${{ env.IMAGE_FRONTEND }}:${{ github.sha }} + - name: Retry build & push frontend без кеша (битый buildcache, #2841) + # См. backend: cache-to опущен намеренно (следующий успешный прогон с + # кешем перезапишет buildcache-тег целиком и самолечит порчу). + if: steps.build.outcome == 'failure' + uses: docker/build-push-action@v6 + with: + context: ./frontend + push: true + build-args: | + NEXT_PUBLIC_GLITCHTIP_DSN=${{ secrets.GLITCHTIP_FRONTEND_DSN }} + NEXT_PUBLIC_ENVIRONMENT=production + tags: | + ${{ env.IMAGE_FRONTEND }}:latest + ${{ env.IMAGE_FRONTEND }}:${{ github.sha }} + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на @@ -627,3 +684,31 @@ jobs: curl -fsS http://localhost:8000/health && break sleep 1 done + + # Честный итог прогона (#2841). ПРОБЛЕМА: `deploy` пропускается своим `if:` + # молча (result=skipped), когда build падает (например, битый blob в + # buildcache роняет `docker/build-push-action` — до ретрая выше, #2841). + # skipped-job НЕ красит прогон явным «FAILED» так, чтобы это было видно на + # первый взгляд — итог выглядит зелёным/нейтральным, хотя прод не обновился. + # Эта job бежит ВСЕГДА (`if: always()`, кроме отмены прогона) и сама падает, + # если deploy не завершился success — неважно, пропущен он (build упал) или + # упал сам (SSH/миграция/health-check). Красная точка встаёт именно там, где + # решение реально принято, а не там, где она случайно оказалась по цепочке if. + deploy-status: + runs-on: ubuntu-latest + needs: [build-backend, build-worker, build-frontend, deploy] + if: always() && !cancelled() + steps: + - name: Итог прогона — деплой обязан быть success, не skipped/failure + run: | + echo "build-backend: ${{ needs.build-backend.result }}" + echo "build-worker: ${{ needs.build-worker.result }}" + echo "build-frontend: ${{ needs.build-frontend.result }}" + echo "deploy: ${{ needs.deploy.result }}" + if [ "${{ needs.deploy.result }}" != "success" ]; then + echo "::error::деплой НЕ прошёл (deploy.result=${{ needs.deploy.result }})." \ + "Прогон должен читаться как FAILED, а не как пропущенный шаг (#2841)." \ + "Смотри логи build-backend/build-worker/build-frontend/deploy выше." + exit 1 + fi + echo "✓ деплой прошёл успешно" -- 2.45.3 From 8bce8cf5aea6bef89250ed276e92e14c4bff54a6 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sat, 15 Aug 2026 18:44:48 +0300 Subject: [PATCH 2/2] fix(ci): fail-safe registry verification + real cache self-heal + honest health-check (#2841 R2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ревью R2 нашёл, что вся безопасность предыдущего фикса держалась на недоказанной поддержке act_runner'ом steps..outcome: если раннер его не заполняет, retry-шаг молча не бежит, continue-on-error проглатывает падение сборки, job зелёный — а деплой тянет старый :latest на прод. - Добавлен engine-agnostic verify-шаг после каждого retry (6 мест, deploy.yml + deploy-tradein.yml): `docker buildx imagetools inspect :` без continue-on-error. Не зависит от того, поддерживает ли раннер outcome — проверяет реальное состояние registry напрямую. Если ни build, ни retry реально не запушили образ — шаг падает и job честно FAILURE независимо от семантики outcome. - Вернул `cache-to` в retry-шаги (6 мест): без него битый buildcache-тег никогда не перезаписывался — retry всегда собирал без cache-to, значит cache-to не выполнялся НИКОГДА, и каждый следующий прогон снова падал на том же cache-from. Заявленное самолечение не работало ни разу. - Health-check в deploy.yml (main-стек) под `set -e` не мог упасть: `curl ... && break` — curl не последняя команда &&-списка, POSIX освобождает такие команды от errexit, цикл дохаживал до sleep (exit 0) даже если curl ни разу не отдал 200. Приведено к паттерну deploy-tradein.yml: явный флаг healthy + `exit 1` после цикла. Подтверждено локальным bash-репро (mock curl, всегда failure): старая версия — exit 0, новая — exit 1; позитивный сценарий не сломан. docker rm -f без -v в SSH-скриптах деплоя не тронут. --- .forgejo/workflows/deploy-tradein.yml | 36 +++++++++++--- .forgejo/workflows/deploy.yml | 67 +++++++++++++++++++++++---- 2 files changed, 87 insertions(+), 16 deletions(-) diff --git a/.forgejo/workflows/deploy-tradein.yml b/.forgejo/workflows/deploy-tradein.yml index 8e1ba10a..640d12ff 100644 --- a/.forgejo/workflows/deploy-tradein.yml +++ b/.forgejo/workflows/deploy-tradein.yml @@ -291,8 +291,10 @@ jobs: ${{ env.IMAGE_BACKEND }}:${{ github.sha }} - name: Retry build & push tradein-backend без кеша (битый buildcache, #2841) - # cache-to тоже опущен: следующий успешный прогон С кешем перезапишет - # buildcache-тег целиком (mode=max) и самолечит порчу. + # cache-from опущен (источник падения), cache-to ОСТАВЛЕН (ревью #2841 R2, + # issue #2): успешный ретрай перезаписывает битый buildcache-тег своими + # слоями (mode=max) — это и есть самолечение. Без cache-to здесь порча + # оставалась навсегда, следующий прогон снова падал на том же cache-from. if: steps.build.outcome == 'failure' uses: docker/build-push-action@v6 with: @@ -303,10 +305,20 @@ jobs: APP_VERSION=${{ needs.changes.outputs.app_version }} BUILD_SHA=${{ needs.changes.outputs.build_sha }} BUILD_DATE=${{ needs.changes.outputs.build_date }} + cache-to: type=registry,ref=${{ env.IMAGE_BACKEND }}:buildcache,mode=max tags: | ${{ env.IMAGE_BACKEND }}:latest ${{ env.IMAGE_BACKEND }}:${{ github.sha }} + - name: Проверить, что tradein-backend:${{ github.sha }} реально в registry (fail-safe, #2841 R2) + # НЕ полагается на семантику steps.build.outcome/continue-on-error раннера — + # проверяет РЕАЛЬНОЕ состояние registry через buildx (уже настроен выше). + # Если act_runner не заполняет outcome, ретрай выше молча НЕ побежит при + # упавшем build — этот шаг единственный это заметит: манифеста с этим SHA + # не будет → шаг падает БЕЗ continue-on-error → job честно FAILURE → deploy + # ниже пропускается вместо накатки старого :latest на прод. + run: docker buildx imagetools inspect ${{ env.IMAGE_BACKEND }}:${{ github.sha }} > /dev/null + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на @@ -414,8 +426,9 @@ jobs: ${{ env.IMAGE_FRONTEND }}:${{ github.sha }} - name: Retry build & push tradein-frontend без кеша (битый buildcache, #2841) - # См. tradein-backend: cache-to опущен намеренно (следующий успешный - # прогон с кешем перезапишет buildcache-тег целиком и самолечит порчу). + # См. tradein-backend (issue #2, ревью R2): cache-from опущен, cache-to + # ОСТАВЛЕН — успешный ретрай перезаписывает битый buildcache-тег своими + # слоями (mode=max), это и есть самолечение. if: steps.build.outcome == 'failure' uses: docker/build-push-action@v6 with: @@ -427,10 +440,15 @@ jobs: NEXT_PUBLIC_APP_VERSION=${{ needs.changes.outputs.app_version }} NEXT_PUBLIC_BUILD_SHA=${{ needs.changes.outputs.build_sha }} NEXT_PUBLIC_BUILD_DATE=${{ needs.changes.outputs.build_date }} + cache-to: type=registry,ref=${{ env.IMAGE_FRONTEND }}:buildcache,mode=max tags: | ${{ env.IMAGE_FRONTEND }}:latest ${{ env.IMAGE_FRONTEND }}:${{ github.sha }} + - name: Проверить, что tradein-frontend:${{ github.sha }} реально в registry (fail-safe, #2841 R2) + # См. tradein-backend выше — не полагается на steps.build.outcome раннера. + run: docker buildx imagetools inspect ${{ env.IMAGE_FRONTEND }}:${{ github.sha }} > /dev/null + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на @@ -518,17 +536,23 @@ jobs: ${{ env.IMAGE_BROWSER }}:${{ github.sha }} - name: Retry build & push tradein-browser без кеша (битый buildcache, #2841) - # См. tradein-backend: cache-to опущен намеренно (следующий успешный - # прогон с кешем перезапишет buildcache-тег целиком и самолечит порчу). + # См. tradein-backend (issue #2, ревью R2): cache-from опущен, cache-to + # ОСТАВЛЕН — успешный ретрай перезаписывает битый buildcache-тег своими + # слоями (mode=max), это и есть самолечение. if: steps.build.outcome == 'failure' uses: docker/build-push-action@v6 with: context: ./tradein-mvp/browser push: true + cache-to: type=registry,ref=${{ env.IMAGE_BROWSER }}:buildcache,mode=max tags: | ${{ env.IMAGE_BROWSER }}:latest ${{ env.IMAGE_BROWSER }}:${{ github.sha }} + - name: Проверить, что tradein-browser:${{ github.sha }} реально в registry (fail-safe, #2841 R2) + # См. tradein-backend выше — не полагается на steps.build.outcome раннера. + run: docker buildx imagetools inspect ${{ env.IMAGE_BROWSER }}:${{ github.sha }} > /dev/null + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index 6c40d79d..4485dd82 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -131,20 +131,35 @@ jobs: ${{ env.IMAGE_BACKEND }}:${{ github.sha }} - name: Retry build & push backend без кеша (битый buildcache, #2841) - # cache-to тоже опущен: следующий успешный прогон С кешем перезапишет - # buildcache-тег целиком (mode=max), это самолечит порчу. Если и retry - # упадёт — шаг красный БЕЗ continue-on-error, job честно FAILURE, и - # deploy ниже корректно пропускается (уже настоящая причина, не кеш). + # cache-from опущен (источник падения), а cache-to ОСТАВЛЕН: успешный + # ретрай пушит свежие слои в buildcache-тег и тем самым сам перезаписывает + # битый blob (mode=max — полная перезапись манифеста). Раньше cache-to был + # опущен и здесь тоже — но следующий обычный прогон опять получает cache-from + # на детерминированно битый тег и падает СНОВА: самолечения не было НИКОГДА + # (ревью #2841 R2, issue #2). Если и retry упадёт — шаг красный БЕЗ + # continue-on-error, job честно FAILURE, и deploy ниже корректно + # пропускается (уже настоящая причина, не кеш). if: steps.build.outcome == 'failure' uses: docker/build-push-action@v6 with: context: ./backend target: runner push: true + cache-to: type=registry,ref=${{ env.IMAGE_BACKEND }}:buildcache,mode=max tags: | ${{ env.IMAGE_BACKEND }}:latest ${{ env.IMAGE_BACKEND }}:${{ github.sha }} + - name: Проверить, что backend:${{ github.sha }} реально в registry (fail-safe, #2841 R2) + # НЕ полагается на семантику steps.build.outcome/continue-on-error раннера — + # проверяет РЕАЛЬНОЕ состояние registry напрямую через buildx (уже настроен + # выше). Если act_runner не заполняет outcome (не проверено живым прогоном, + # см. ревью), ретрай выше молча НЕ побежит при упавшем build, а этот шаг — + # единственный, кто это заметит: манифеста с этим SHA не будет → шаг падает + # БЕЗ continue-on-error → job честно FAILURE → deploy ниже пропускается + # вместо накатки старого :latest на прод. + run: docker buildx imagetools inspect ${{ env.IMAGE_BACKEND }}:${{ github.sha }} > /dev/null + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на @@ -231,18 +246,27 @@ jobs: ${{ env.IMAGE_WORKER }}:${{ github.sha }} - name: Retry build & push worker без кеша (битый buildcache, #2841) - # См. backend: cache-to опущен намеренно (следующий успешный прогон с - # кешем перезапишет buildcache-тег целиком и самолечит порчу). + # См. backend (issue #2, ревью R2): cache-from опущен, cache-to ОСТАВЛЕН — + # успешный ретрай перезаписывает битый buildcache-тег своими слоями + # (mode=max), это и есть самолечение. Без cache-to здесь порча оставалась + # навсегда — следующий прогон снова падал на том же cache-from. if: steps.build.outcome == 'failure' uses: docker/build-push-action@v6 with: context: ./backend target: runner-with-chromium push: true + cache-to: type=registry,ref=${{ env.IMAGE_WORKER }}:buildcache,mode=max tags: | ${{ env.IMAGE_WORKER }}:latest ${{ env.IMAGE_WORKER }}:${{ github.sha }} + - name: Проверить, что worker:${{ github.sha }} реально в registry (fail-safe, #2841 R2) + # См. backend выше — не полагается на steps.build.outcome раннера, проверяет + # реальное состояние registry, чтобы молча пропущенный ретрай (если outcome + # не поддержан) честно уронил job вместо зелёного прогона с непушнутым образом. + run: docker buildx imagetools inspect ${{ env.IMAGE_WORKER }}:${{ github.sha }} > /dev/null + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на @@ -331,8 +355,10 @@ jobs: ${{ env.IMAGE_FRONTEND }}:${{ github.sha }} - name: Retry build & push frontend без кеша (битый buildcache, #2841) - # См. backend: cache-to опущен намеренно (следующий успешный прогон с - # кешем перезапишет buildcache-тег целиком и самолечит порчу). + # См. backend (issue #2, ревью R2): cache-from опущен, cache-to ОСТАВЛЕН — + # успешный ретрай перезаписывает битый buildcache-тег своими слоями + # (mode=max), это и есть самолечение. Без cache-to здесь порча оставалась + # навсегда — следующий прогон снова падал на том же cache-from. if: steps.build.outcome == 'failure' uses: docker/build-push-action@v6 with: @@ -341,10 +367,17 @@ jobs: build-args: | NEXT_PUBLIC_GLITCHTIP_DSN=${{ secrets.GLITCHTIP_FRONTEND_DSN }} NEXT_PUBLIC_ENVIRONMENT=production + cache-to: type=registry,ref=${{ env.IMAGE_FRONTEND }}:buildcache,mode=max tags: | ${{ env.IMAGE_FRONTEND }}:latest ${{ env.IMAGE_FRONTEND }}:${{ github.sha }} + - name: Проверить, что frontend:${{ github.sha }} реально в registry (fail-safe, #2841 R2) + # См. backend выше — не полагается на steps.build.outcome раннера, проверяет + # реальное состояние registry, чтобы молча пропущенный ретрай (если outcome + # не поддержан) честно уронил job вместо зелёного прогона с непушнутым образом. + run: docker buildx imagetools inspect ${{ env.IMAGE_FRONTEND }}:${{ github.sha }} > /dev/null + - name: Убрать buildx-билдер (#2869 — иначе копятся по одному на прогон) # setup-buildx-action создаёт билдер `docker-container` на КАЖДЫЙ прогон. # Его post-step под Forgejo act_runner не срабатывает, поэтому к 13.08 на @@ -679,11 +712,25 @@ jobs: docker image prune -af || true docker builder prune -af || true - # Health check + # Health check — деплой ВАЛИТСЯ, если backend не поднялся (см. #2214, + # уже сделано так в deploy-tradein.yml; ревью #2841 R2 issue #3). + # `curl ... && break` под set -e НЕ мог провалить скрипт: curl — не + # последняя команда &&-списка, а POSIX прямо освобождает от errexit + # все команды AND/OR-списка кроме последней. После 30 неуспешных + # попыток цикл завершался кодом последнего sleep (0) — скрипт тихо + # продолжался, деплой уходил success с мёртвым бэкендом. + healthy="" for i in $(seq 1 30); do - curl -fsS http://localhost:8000/health && break + if curl -fsS http://localhost:8000/health >/dev/null 2>&1; then + healthy="yes"; break + fi sleep 1 done + if [ -z "$healthy" ]; then + echo "ERROR: backend не ответил на /health за 30s — деплой FAILED" + exit 1 + fi + echo "→ backend healthy на /health." # Честный итог прогона (#2841). ПРОБЛЕМА: `deploy` пропускается своим `if:` # молча (result=skipped), когда build падает (например, битый blob в -- 2.45.3