From 8d9cd201b415d1d519fed4de72aaa93c1db370d2 Mon Sep 17 00:00:00 2001 From: nns <278048682+nurdotnet@users.noreply.github.com> Date: Sat, 25 Apr 2026 13:22:31 +0500 Subject: [PATCH] =?UTF-8?q?ci(docker):=20split=20into=20docker-api.yml=20+?= =?UTF-8?q?=20docker-web.yml=20=E2=80=94=20=D0=BD=D0=B5=D0=B7=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D1=81=D0=B8=D0=BC=D1=8B=D0=B5=20pipeline'=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit В предыдущей попытке runner v6.2.2 не подружился с cross-job outputs — job changes падал «'runs-on' key not defined in Docker Images/changes» и оба следующих job уходили в skipped. Откатываю на надёжный путь: два отдельных workflow с paths-фильтром на уровне триггера. - docker-api.yml: триггерится на src/food-market.api/**, application/**, domain/**, infrastructure/**, shared/**, sln, Dockerfile.api, compose. Билдит buildx с registry-cache, пуллит и пересоздаёт ТОЛЬКО api (`docker compose up -d --no-deps api`). - docker-web.yml: триггерится на src/food-market.web/**, Dockerfile.web, nginx.conf, compose. Делает то же для web. - .env стенда теперь идемпотентный — оба тэга всегда :latest, после push образа buildx обновляет latest, pull тянет свежий. - Telegram отдельные сообщения «stage api deployed» / «stage web deployed» с SHA — понятно что именно прилетело. Push, который не задевает ни api/ ни web/ исходники (только md/docs), вообще не запускает workflow — экономия очевидна. Co-Authored-By: Claude Opus 4.7 (1M context) --- .forgejo/workflows/docker-api.yml | 103 ++++++++++++++++ .forgejo/workflows/docker-web.yml | 97 +++++++++++++++ .forgejo/workflows/docker.yml | 191 ------------------------------ 3 files changed, 200 insertions(+), 191 deletions(-) create mode 100644 .forgejo/workflows/docker-api.yml create mode 100644 .forgejo/workflows/docker-web.yml delete mode 100644 .forgejo/workflows/docker.yml diff --git a/.forgejo/workflows/docker-api.yml b/.forgejo/workflows/docker-api.yml new file mode 100644 index 0000000..aa0edd1 --- /dev/null +++ b/.forgejo/workflows/docker-api.yml @@ -0,0 +1,103 @@ +name: Docker API + +on: + push: + branches: [main] + paths: + - 'src/food-market.api/**' + - 'src/food-market.application/**' + - 'src/food-market.domain/**' + - 'src/food-market.infrastructure/**' + - 'src/food-market.shared/**' + - 'deploy/Dockerfile.api' + - 'deploy/docker-compose.yml' + - '.forgejo/workflows/docker-api.yml' + - 'food-market.sln' + workflow_dispatch: + +env: + LOCAL_REGISTRY: 127.0.0.1:5001 + +jobs: + build: + name: Build + push API + runs-on: [self-hosted, linux] + steps: + - uses: actions/checkout@v4 + + - name: Build + push (buildx with registry cache) + env: + SHA: ${{ github.sha }} + run: | + docker buildx create --use --name fmbuilder --driver docker-container 2>/dev/null \ + || docker buildx use fmbuilder + docker buildx build \ + -f deploy/Dockerfile.api \ + -t $LOCAL_REGISTRY/food-market-api:$SHA \ + -t $LOCAL_REGISTRY/food-market-api:latest \ + --cache-from type=registry,ref=$LOCAL_REGISTRY/food-market-api:buildcache \ + --cache-to type=registry,ref=$LOCAL_REGISTRY/food-market-api:buildcache,mode=max \ + --push . + + deploy: + name: Deploy API on stage + needs: build + runs-on: [self-hosted, linux] + steps: + - uses: actions/checkout@v4 + + - name: Update compose + .env + env: + PGPASS: ${{ secrets.STAGE_POSTGRES_PASSWORD }} + run: | + # Стенд использует :latest для обоих сервисов, .env переписываем + # идемпотентно — без затирания тэга соседнего сервиса. + cat > /home/nns/food-market-stage/deploy/.env < /dev/null + + - name: Notify Telegram on failure + if: failure() + env: + BOT: ${{ secrets.TELEGRAM_BOT_TOKEN }} + CHAT: ${{ secrets.TELEGRAM_CHAT_ID }} + SHA: ${{ github.sha }} + run: | + curl -sS -X POST "https://api.telegram.org/bot$BOT/sendMessage" \ + --data-urlencode "chat_id=$CHAT" \ + --data-urlencode "text=❌ stage api deploy FAILED — ${SHA:0:7}" \ + > /dev/null diff --git a/.forgejo/workflows/docker-web.yml b/.forgejo/workflows/docker-web.yml new file mode 100644 index 0000000..be13c08 --- /dev/null +++ b/.forgejo/workflows/docker-web.yml @@ -0,0 +1,97 @@ +name: Docker Web + +on: + push: + branches: [main] + paths: + - 'src/food-market.web/**' + - 'deploy/Dockerfile.web' + - 'deploy/nginx.conf' + - 'deploy/docker-compose.yml' + - '.forgejo/workflows/docker-web.yml' + workflow_dispatch: + +env: + LOCAL_REGISTRY: 127.0.0.1:5001 + +jobs: + build: + name: Build + push Web + runs-on: [self-hosted, linux] + steps: + - uses: actions/checkout@v4 + + - name: Build + push (buildx with registry cache) + env: + SHA: ${{ github.sha }} + run: | + docker buildx create --use --name fmbuilder --driver docker-container 2>/dev/null \ + || docker buildx use fmbuilder + docker buildx build \ + -f deploy/Dockerfile.web \ + -t $LOCAL_REGISTRY/food-market-web:$SHA \ + -t $LOCAL_REGISTRY/food-market-web:latest \ + --cache-from type=registry,ref=$LOCAL_REGISTRY/food-market-web:buildcache \ + --cache-to type=registry,ref=$LOCAL_REGISTRY/food-market-web:buildcache,mode=max \ + --push . + + deploy: + name: Deploy Web on stage + needs: build + runs-on: [self-hosted, linux] + steps: + - uses: actions/checkout@v4 + + - name: Update compose + .env + env: + PGPASS: ${{ secrets.STAGE_POSTGRES_PASSWORD }} + run: | + cat > /home/nns/food-market-stage/deploy/.env < /dev/null + + - name: Notify Telegram on failure + if: failure() + env: + BOT: ${{ secrets.TELEGRAM_BOT_TOKEN }} + CHAT: ${{ secrets.TELEGRAM_CHAT_ID }} + SHA: ${{ github.sha }} + run: | + curl -sS -X POST "https://api.telegram.org/bot$BOT/sendMessage" \ + --data-urlencode "chat_id=$CHAT" \ + --data-urlencode "text=❌ stage web deploy FAILED — ${SHA:0:7}" \ + > /dev/null diff --git a/.forgejo/workflows/docker.yml b/.forgejo/workflows/docker.yml deleted file mode 100644 index e0e939d..0000000 --- a/.forgejo/workflows/docker.yml +++ /dev/null @@ -1,191 +0,0 @@ -name: Docker Images - -on: - push: - branches: [main] - paths-ignore: - - '**.md' - - 'docs/**' - - '.github/**' - workflow_dispatch: - -env: - LOCAL_REGISTRY: 127.0.0.1:5001 - -jobs: - # Решает что именно изменилось в этом push'е, чтобы ниже собирать только нужные - # образы. Outputs `api` / `web` = "true"|"false". При workflow_dispatch — оба true. - changes: - name: Detect changes - runs-on: [self-hosted, linux] - outputs: - api: ${{ steps.filter.outputs.api }} - web: ${{ steps.filter.outputs.web }} - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - - id: filter - run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - echo "api=true" >> "$GITHUB_OUTPUT" - echo "web=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - base=$(git rev-parse HEAD~1 2>/dev/null || git rev-parse HEAD) - changed=$(git diff --name-only "$base" HEAD) - echo "Changed files since $base:" - echo "$changed" - api=false; web=false - while IFS= read -r f; do - [ -z "$f" ] && continue - case "$f" in - src/food-market.api/*|\ - src/food-market.application/*|\ - src/food-market.domain/*|\ - src/food-market.infrastructure/*|\ - src/food-market.shared/*|\ - deploy/Dockerfile.api|\ - deploy/docker-compose.yml|\ - .forgejo/workflows/docker.yml|\ - food-market.sln) - api=true ;; - esac - case "$f" in - src/food-market.web/*|\ - deploy/Dockerfile.web|\ - deploy/nginx.conf|\ - deploy/docker-compose.yml|\ - .forgejo/workflows/docker.yml) - web=true ;; - esac - done <<< "$changed" - echo "api=$api" >> "$GITHUB_OUTPUT" - echo "web=$web" >> "$GITHUB_OUTPUT" - echo "Result: api=$api web=$web" - - api: - name: API image - needs: changes - if: needs.changes.outputs.api == 'true' - runs-on: [self-hosted, linux] - steps: - - uses: actions/checkout@v4 - - - name: Build + push API (buildx with registry cache) - env: - SHA: ${{ github.sha }} - run: | - docker buildx create --use --name fmbuilder --driver docker-container 2>/dev/null \ - || docker buildx use fmbuilder - docker buildx build \ - -f deploy/Dockerfile.api \ - -t $LOCAL_REGISTRY/food-market-api:$SHA \ - -t $LOCAL_REGISTRY/food-market-api:latest \ - --cache-from type=registry,ref=$LOCAL_REGISTRY/food-market-api:buildcache \ - --cache-to type=registry,ref=$LOCAL_REGISTRY/food-market-api:buildcache,mode=max \ - --push . - - web: - name: Web image - needs: changes - if: needs.changes.outputs.web == 'true' - runs-on: [self-hosted, linux] - steps: - - uses: actions/checkout@v4 - - - name: Build + push Web (buildx with registry cache) - env: - SHA: ${{ github.sha }} - run: | - docker buildx create --use --name fmbuilder --driver docker-container 2>/dev/null \ - || docker buildx use fmbuilder - docker buildx build \ - -f deploy/Dockerfile.web \ - -t $LOCAL_REGISTRY/food-market-web:$SHA \ - -t $LOCAL_REGISTRY/food-market-web:latest \ - --cache-from type=registry,ref=$LOCAL_REGISTRY/food-market-web:buildcache \ - --cache-to type=registry,ref=$LOCAL_REGISTRY/food-market-web:buildcache,mode=max \ - --push . - - deploy-stage: - name: Deploy stage - needs: [changes, api, web] - # always() позволяет deploy запуститься даже если api/web был пропущен. - # Запускаем когда хотя бы один из api/web реально пересобрался ИЛИ менялся compose. - if: | - always() - && (needs.api.result == 'success' || needs.api.result == 'skipped') - && (needs.web.result == 'success' || needs.web.result == 'skipped') - && (needs.changes.outputs.api == 'true' || needs.changes.outputs.web == 'true') - runs-on: [self-hosted, linux] - steps: - - uses: actions/checkout@v4 - - - name: Write .env + copy compose (runner = stage host) - env: - SHA: ${{ github.sha }} - PGPASS: ${{ secrets.STAGE_POSTGRES_PASSWORD }} - run: | - # Если в этом ране какой-то из образов не пересобирался, используем :latest — - # текущий compose и так указывает на 127.0.0.1:5001/food-market-{api,web}:$TAG. - api_tag="$SHA"; web_tag="$SHA" - [ "${{ needs.changes.outputs.api }}" = "true" ] || api_tag=latest - [ "${{ needs.changes.outputs.web }}" = "true" ] || web_tag=latest - cat > /home/nns/food-market-stage/deploy/.env < /dev/null - - - name: Notify Telegram on failure - if: failure() - env: - BOT: ${{ secrets.TELEGRAM_BOT_TOKEN }} - CHAT: ${{ secrets.TELEGRAM_CHAT_ID }} - SHA: ${{ github.sha }} - run: | - curl -sS -X POST "https://api.telegram.org/bot$BOT/sendMessage" \ - --data-urlencode "chat_id=$CHAT" \ - --data-urlencode "text=❌ stage deploy FAILED — ${SHA:0:7}" \ - > /dev/null