From 50c6d671b8d024a7e0d82d1124c86cac25695a77 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: Thu, 16 Jul 2026 11:31:22 +0300 Subject: [PATCH] =?UTF-8?q?feat(ci):=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5?= =?UTF-8?q?=D0=B4=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B?= =?UTF-8?q?=D0=B9=20deploy=20IFT=20->=20E2E=20->=20stage=20->=20E2E=20(cor?= =?UTF-8?q?e).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stage после API CT remote на IFT; после stage — smoke-core + полный CT на stage. Co-authored-by: Cursor --- .gitea/workflows/deploy-stage.yml | 4 +- .gitea/workflows/e2e-ift.yml | 53 ++++++++++++++++++++++ .gitea/workflows/e2e-stage.yml | 53 ++++++++++++++++++++++ scripts/load-stand-api-env.sh | 50 +++++++++++++++++++++ scripts/run-stand-api-tests.sh | 73 +++++++++++++++++++++++++++++++ 5 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 .gitea/workflows/e2e-ift.yml create mode 100644 .gitea/workflows/e2e-stage.yml create mode 100644 scripts/load-stand-api-env.sh create mode 100644 scripts/run-stand-api-tests.sh diff --git a/.gitea/workflows/deploy-stage.yml b/.gitea/workflows/deploy-stage.yml index 54b7d19..6a6c3b7 100644 --- a/.gitea/workflows/deploy-stage.yml +++ b/.gitea/workflows/deploy-stage.yml @@ -1,9 +1,9 @@ name: Deploy stage (core) -# Auto после успешного CI на master: тот же sha-*, плюс floating :stage. +# Auto после успешного E2E IFT на master: promote sha-* → :stage и SSH deploy. on: workflow_run: - workflows: [CI] + workflows: ["E2E IFT (core)"] types: [completed] branches: [master, main] workflow_dispatch: diff --git a/.gitea/workflows/e2e-ift.yml b/.gitea/workflows/e2e-ift.yml new file mode 100644 index 0000000..c7de874 --- /dev/null +++ b/.gitea/workflows/e2e-ift.yml @@ -0,0 +1,53 @@ +name: E2E IFT (core) + +# После успешного Deploy IFT — smoke-core на стенде + полный API CT (remote) против живого IFT. +# Учётки — из /opt/eventhub-ift/.env по SSH (ADMIN_*). +on: + workflow_run: + workflows: ["Deploy IFT (core)"] + types: [completed] + workflow_dispatch: + +concurrency: + group: eventhub-back-e2e-ift + cancel-in-progress: false + +env: + BUILDKIT_PROGRESS: quiet + DOCKER_BUILDKIT: 1 + +jobs: + ift-e2e: + 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 }} + + - name: Wait for Docker daemon + run: bash scripts/ensure-docker-daemon.sh 240 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Load API test credentials from IFT .env + env: + SSH_HOST: ${{ secrets.IFT_SSH_HOST }} + SSH_USER: ${{ secrets.IFT_SSH_USER }} + SSH_KEY: ${{ secrets.IFT_SSH_PRIVATE_KEY }} + run: bash scripts/load-stand-api-env.sh ift + + - name: E2E IFT (full API suite) + env: + SSH_HOST: ${{ secrets.IFT_SSH_HOST }} + SSH_USER: ${{ secrets.IFT_SSH_USER }} + SSH_KEY: ${{ secrets.IFT_SSH_PRIVATE_KEY }} + API_HOST: ${{ secrets.E2E_IFT_API_HOST }} + ADMIN_API_HOST: ${{ secrets.E2E_IFT_ADMIN_API_HOST }} + WS_HOST: ${{ secrets.E2E_IFT_WS_HOST }} + ADMIN_WS_HOST: ${{ secrets.E2E_IFT_ADMIN_WS_HOST }} + run: bash scripts/run-stand-api-tests.sh ift diff --git a/.gitea/workflows/e2e-stage.yml b/.gitea/workflows/e2e-stage.yml new file mode 100644 index 0000000..c7e520d --- /dev/null +++ b/.gitea/workflows/e2e-stage.yml @@ -0,0 +1,53 @@ +name: E2E stage (core) + +# После успешного Deploy stage — smoke-core на стенде + полный API CT (remote) против живого stage. +# Учётки — из /opt/eventhub-stage/.env по SSH (ADMIN_*). +on: + workflow_run: + workflows: ["Deploy stage (core)"] + types: [completed] + workflow_dispatch: + +concurrency: + group: eventhub-back-e2e-stage + cancel-in-progress: false + +env: + BUILDKIT_PROGRESS: quiet + DOCKER_BUILDKIT: 1 + +jobs: + stage-e2e: + 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 }} + + - name: Wait for Docker daemon + run: bash scripts/ensure-docker-daemon.sh 240 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Load API test credentials from stage .env + env: + SSH_HOST: ${{ secrets.STAGE_SSH_HOST }} + SSH_USER: ${{ secrets.STAGE_SSH_USER }} + SSH_KEY: ${{ secrets.STAGE_SSH_PRIVATE_KEY }} + run: bash scripts/load-stand-api-env.sh stage + + - name: E2E stage (full API suite) + env: + SSH_HOST: ${{ secrets.STAGE_SSH_HOST }} + SSH_USER: ${{ secrets.STAGE_SSH_USER }} + SSH_KEY: ${{ secrets.STAGE_SSH_PRIVATE_KEY }} + API_HOST: ${{ secrets.E2E_STAGE_API_HOST }} + ADMIN_API_HOST: ${{ secrets.E2E_STAGE_ADMIN_API_HOST }} + WS_HOST: ${{ secrets.E2E_STAGE_WS_HOST }} + ADMIN_WS_HOST: ${{ secrets.E2E_STAGE_ADMIN_WS_HOST }} + run: bash scripts/run-stand-api-tests.sh stage diff --git a/scripts/load-stand-api-env.sh b/scripts/load-stand-api-env.sh new file mode 100644 index 0000000..ff45d61 --- /dev/null +++ b/scripts/load-stand-api-env.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# Load admin API test credentials from stand .env via SSH into GITHUB_ENV. +# Usage: load-stand-api-env.sh +set -euo pipefail + +STAND="${1:?stand: ift|stage}" +case "${STAND}" in + ift) + SSH_HOST="${SSH_HOST:?IFT_SSH_HOST required}" + SSH_USER="${SSH_USER:?IFT_SSH_USER required}" + SSH_KEY="${SSH_KEY:?IFT_SSH_PRIVATE_KEY required}" + ENV_FILE="/opt/eventhub-ift/.env" + ;; + stage) + SSH_HOST="${SSH_HOST:?STAGE_SSH_HOST required}" + SSH_USER="${SSH_USER:?STAGE_SSH_USER required}" + SSH_KEY="${SSH_KEY:?STAGE_SSH_PRIVATE_KEY required}" + ENV_FILE="/opt/eventhub-stage/.env" + ;; + *) + echo "Unknown stand: ${STAND}" + exit 1 + ;; +esac + +KEY="$(mktemp)" +printf '%s\n' "${SSH_KEY}" > "${KEY}" +chmod 600 "${KEY}" + +REMOTE="$(ssh -i "${KEY}" -o BatchMode=yes -o StrictHostKeyChecking=accept-new \ + "${SSH_USER}@${SSH_HOST}" \ + "grep -E '^(ADMIN_|SMOKE_ADMIN_)' ${ENV_FILE} || true")" +rm -f "${KEY}" + +if [[ -z "${REMOTE}" ]]; then + echo "::error::В ${ENV_FILE} нет ADMIN_* / SMOKE_ADMIN_*" + exit 1 +fi + +OUT="${GITHUB_ENV:-}" +while IFS= read -r line; do + [[ -n "${line}" ]] || continue + if [[ -n "${OUT}" ]]; then + echo "${line}" >> "${OUT}" + else + printf '%s\n' "${line}" >> "$(mktemp)" + fi +done <<< "${REMOTE}" + +echo "Loaded API test credentials from ${ENV_FILE}" diff --git a/scripts/run-stand-api-tests.sh b/scripts/run-stand-api-tests.sh new file mode 100644 index 0000000..944d612 --- /dev/null +++ b/scripts/run-stand-api-tests.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# Full API CT suite against live stand (remote) + smoke-core on stand host. +# Usage: run-stand-api-tests.sh +# Requires: ADMIN_* from load-stand-api-env.sh; SSH_* for smoke-core on stand. +set -euo pipefail + +STAND="${1:?stand: ift|stage}" +case "${STAND}" in + ift) + API_HOST="${API_HOST:-https://api.ift.eventhub.local}" + ADMIN_API_HOST="${ADMIN_API_HOST:-https://admin-api.ift.eventhub.local}" + WS_HOST="${WS_HOST:-wss://api.ift.eventhub.local}" + ADMIN_WS_HOST="${ADMIN_WS_HOST:-wss://admin-api.ift.eventhub.local}" + DEVOPS_SMOKE="/opt/eventhub-ift/devops/scripts/smoke-core.sh" + ;; + stage) + API_HOST="${API_HOST:-https://api.stage.eventhub.local}" + ADMIN_API_HOST="${ADMIN_API_HOST:-https://admin-api.stage.eventhub.local}" + WS_HOST="${WS_HOST:-wss://api.stage.eventhub.local}" + ADMIN_WS_HOST="${ADMIN_WS_HOST:-wss://admin-api.stage.eventhub.local}" + DEVOPS_SMOKE="/opt/eventhub-stage/devops/scripts/smoke-core.sh" + ;; + *) + echo "Unknown stand: ${STAND}" + exit 1 + ;; +esac + +if [[ -n "${SSH_HOST:-}" && -n "${SSH_USER:-}" && -n "${SSH_KEY:-}" ]]; then + KEY="$(mktemp)" + printf '%s\n' "${SSH_KEY}" > "${KEY}" + chmod 600 "${KEY}" + echo "=== smoke-core on ${STAND} (via SSH) ===" + ssh -i "${KEY}" -o BatchMode=yes -o StrictHostKeyChecking=accept-new \ + "${SSH_USER}@${SSH_HOST}" "bash ${DEVOPS_SMOKE} ${STAND}" + rm -f "${KEY}" +else + echo "::warning::SSH_* not set — skip smoke-core on stand host" +fi + +echo "=== API CT remote (${STAND}) ===" +echo " API_HOST=${API_HOST}" +echo " ADMIN_API_HOST=${ADMIN_API_HOST}" + +if ! docker image inspect eventhub-api-tests:local >/dev/null 2>&1; then + bash scripts/retry-docker-build.sh 5 60 -- \ + docker buildx build \ + --network=host \ + --file docker/ApiTests.Dockerfile \ + --tag eventhub-api-tests:local \ + --load \ + --provenance=false \ + --sbom=false \ + . +fi + +docker run --rm --network=host \ + -e CT_MODE=remote \ + -e "API_HOST=${API_HOST}" \ + -e "ADMIN_API_HOST=${ADMIN_API_HOST}" \ + -e "WS_HOST=${WS_HOST}" \ + -e "ADMIN_WS_HOST=${ADMIN_WS_HOST}" \ + -e "ADMIN_SUPER_EMAIL=${ADMIN_SUPER_EMAIL:-}" \ + -e "ADMIN_SUPER_PASSWORD=${ADMIN_SUPER_PASSWORD:-}" \ + -e "ADMIN_EMAIL=${ADMIN_EMAIL:-}" \ + -e "ADMIN_PASSWORD=${ADMIN_PASSWORD:-}" \ + -e "ADMIN_MODER_EMAIL=${ADMIN_MODER_EMAIL:-}" \ + -e "ADMIN_MODER_PASSWORD=${ADMIN_MODER_PASSWORD:-}" \ + -e "ADMIN_SUPPORT_EMAIL=${ADMIN_SUPPORT_EMAIL:-}" \ + -e "ADMIN_SUPPORT_PASSWORD=${ADMIN_SUPPORT_PASSWORD:-}" \ + eventhub-api-tests:local + +echo "=== API CT passed (${STAND}) ==="