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