From 9b48b165ba5364d154eb7856855978e754febc74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9=20=D0=A1=D0=B0?= =?UTF-8?q?=D0=B1=D0=B8=D0=BB=D0=B8=D0=BD?= Date: Fri, 10 Jul 2026 12:55:01 +0300 Subject: [PATCH] CI: build-once pipeline, deploy IFT after CI with conditional aux images. Co-authored-by: Cursor --- .gitea/workflows/ci.yml | 19 +++++++ .gitea/workflows/deploy-ift.yml | 88 ++++++++++++++----------------- .gitea/workflows/deploy-stage.yml | 2 +- scripts/publish-ift-aux-images.sh | 45 ++++++++++++++++ 4 files changed, 104 insertions(+), 50 deletions(-) create mode 100644 scripts/publish-ift-aux-images.sh diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 58bbd4d..d271c5b 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -13,6 +13,7 @@ concurrency: env: BUILDKIT_PROGRESS: quiet DOCKER_BUILDKIT: 1 + REGISTRY: git.sabilin.com/eventhub jobs: test: @@ -46,3 +47,21 @@ jobs: - name: Run API tests (local CT) run: docker run --rm eventhub-api-tests:latest + + - name: Login to registry + if: github.event_name == 'push' + uses: docker/login-action@v2 + with: + registry: git.sabilin.com + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Push tested eventhub image + if: github.event_name == 'push' + run: | + set -euo pipefail + TAG="sha-${GITHUB_SHA:0:12}" + docker tag eventhub:latest "${REGISTRY}/eventhub:${TAG}" + docker tag eventhub:latest "${REGISTRY}/eventhub:ift" + docker push "${REGISTRY}/eventhub:${TAG}" + docker push "${REGISTRY}/eventhub:ift" diff --git a/.gitea/workflows/deploy-ift.yml b/.gitea/workflows/deploy-ift.yml index 745d341..6a67050 100644 --- a/.gitea/workflows/deploy-ift.yml +++ b/.gitea/workflows/deploy-ift.yml @@ -1,12 +1,14 @@ name: Deploy IFT (core) on: - push: + workflow_run: + workflows: [CI] + types: [completed] branches: [master, main] workflow_dispatch: concurrency: - group: eventhub-back-${{ github.ref }} + group: eventhub-back-deploy-ift cancel-in-progress: false env: @@ -16,14 +18,27 @@ env: jobs: deploy-ift-core: + if: | + github.event_name == 'workflow_dispatch' || + (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') runs-on: ubuntu-latest timeout-minutes: 90 steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }} + fetch-depth: 2 - name: Compute image tag id: meta - run: echo "tag=sha-${GITHUB_SHA::12}" >> "$GITHUB_OUTPUT" + run: | + set -euo pipefail + if [ "${{ github.event_name }}" = "workflow_run" ]; then + SHA="${{ github.event.workflow_run.head_sha }}" + else + SHA="${GITHUB_SHA}" + fi + echo "tag=sha-${SHA:0:12}" >> "$GITHUB_OUTPUT" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -35,53 +50,28 @@ jobs: username: ${{ secrets.REGISTRY_USER }} password: ${{ secrets.REGISTRY_PASSWORD }} - - name: Build and push eventhub - uses: docker/build-push-action@v5 - with: - context: . - file: docker/Dockerfile - push: true - tags: | - ${{ env.REGISTRY }}/eventhub:${{ steps.meta.outputs.tag }} - ${{ env.REGISTRY }}/eventhub:ift - provenance: false - sbom: false + - name: Verify eventhub image from CI + run: | + set -euo pipefail + TAG="${{ steps.meta.outputs.tag }}" + if ! docker buildx imagetools inspect "${REGISTRY}/eventhub:${TAG}" >/dev/null 2>&1; then + echo "Образ eventhub:${TAG} не найден — сначала должен пройти CI (build + test + push)." + exit 1 + fi + echo "OK: eventhub:${TAG} (из CI, без пересборки)" - - name: Build and push traefik-coraza - uses: docker/build-push-action@v5 - with: - context: docker/traefik - file: docker/traefik/Dockerfile - push: true - tags: | - ${{ env.REGISTRY }}/traefik-coraza:${{ steps.meta.outputs.tag }} - ${{ env.REGISTRY }}/traefik-coraza:ift - provenance: false - sbom: false - - - name: Build and push fallback - uses: docker/build-push-action@v5 - with: - context: docker/fallback - file: docker/fallback/Dockerfile - push: true - tags: | - ${{ env.REGISTRY }}/fallback:${{ steps.meta.outputs.tag }} - ${{ env.REGISTRY }}/fallback:ift - provenance: false - sbom: false - - - name: Build and push bot-emulator - uses: docker/build-push-action@v5 - with: - context: . - file: test/emulate_users/Dockerfile - push: true - tags: | - ${{ env.REGISTRY }}/bot-emulator-users:${{ steps.meta.outputs.tag }} - ${{ env.REGISTRY }}/bot-emulator-users:ift - provenance: false - sbom: false + # Вспомогательные образы Back: rebuild при изменениях, иначе promote :ift -> sha-*. + - name: Publish auxiliary images + run: | + set -euo pipefail + TAG="${{ steps.meta.outputs.tag }}" + if [ "${{ github.event_name }}" = "workflow_run" ]; then + HEAD="${{ github.event.workflow_run.head_sha }}" + else + HEAD="${GITHUB_SHA}" + fi + FILES="$(git diff --name-only "${HEAD}^" "${HEAD}" 2>/dev/null || git show --pretty="" --name-only "${HEAD}")" + bash scripts/publish-ift-aux-images.sh "${REGISTRY}" "${TAG}" "${FILES}" - name: Deploy core on IFT via SSH uses: appleboy/ssh-action@v0.1.5 diff --git a/.gitea/workflows/deploy-stage.yml b/.gitea/workflows/deploy-stage.yml index d116328..dfc021c 100644 --- a/.gitea/workflows/deploy-stage.yml +++ b/.gitea/workflows/deploy-stage.yml @@ -36,7 +36,7 @@ jobs: set -euo pipefail SHA="${{ steps.meta.outputs.sha_tag }}" REL="${{ steps.meta.outputs.release_tag }}" - IMAGES="eventhub traefik-coraza fallback bot-emulator-users" + IMAGES="eventhub traefik-coraza fallback bot-emulator-users observer-web logrotate" missing="" for img in ${IMAGES}; do if ! docker buildx imagetools inspect "${REGISTRY}/${img}:${SHA}" >/dev/null 2>&1; then diff --git a/scripts/publish-ift-aux-images.sh b/scripts/publish-ift-aux-images.sh new file mode 100644 index 0000000..54df78b --- /dev/null +++ b/scripts/publish-ift-aux-images.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# Публикация вспомогательных образов IFT: rebuild при изменениях, иначе promote :ift -> sha-*. +# Usage: publish-ift-aux-images.sh +set -euo pipefail + +REGISTRY="${1:?registry}" +TAG="${2:?sha-tag}" +FILES="${3:-}" + +publish_image() { + local name="$1" + local path_pattern="$2" + local context="$3" + local dockerfile="$4" + + if docker buildx imagetools inspect "${REGISTRY}/${name}:${TAG}" >/dev/null 2>&1; then + echo "== ${name}: ${TAG} already in registry, skip" + return + fi + + if echo "${FILES}" | grep -qE "${path_pattern}"; then + echo "== ${name}: rebuild (matched: ${path_pattern})" + docker buildx build -f "${dockerfile}" --push \ + -t "${REGISTRY}/${name}:${TAG}" \ + -t "${REGISTRY}/${name}:ift" \ + --provenance=false --sbom=false \ + "${context}" + return + fi + + echo "== ${name}: unchanged, promote :ift -> ${TAG}" + if ! docker buildx imagetools inspect "${REGISTRY}/${name}:ift" >/dev/null 2>&1; then + echo "ERROR: ${name}:ift not found and no matching changes in this commit" + exit 1 + fi + docker buildx imagetools create \ + -t "${REGISTRY}/${name}:${TAG}" \ + "${REGISTRY}/${name}:ift" +} + +publish_image traefik-coraza '^docker/traefik/' docker/traefik docker/traefik/Dockerfile +publish_image fallback '^docker/fallback/' docker/fallback docker/fallback/Dockerfile +publish_image bot-emulator-users '^test/emulate_users/' . test/emulate_users/Dockerfile +publish_image observer-web '^docker/ObserverWeb\.Dockerfile|^docker/observer_web/' . docker/ObserverWeb.Dockerfile +publish_image logrotate '^docker/logrotate/' docker/logrotate docker/logrotate/Dockerfile