From 0e60c0e591f314402757c2d85ecaa345bfd1a1cd Mon Sep 17 00:00:00 2001 From: Antigravity Date: Sun, 17 May 2026 09:59:33 +0000 Subject: [PATCH] =?UTF-8?q?fix(deploy):=20d=C3=A9clencher=20apr=C3=A8s=20C?= =?UTF-8?q?I=20et=20v=C3=A9rifier=20le=20commit=20d=C3=A9ploy=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le job deploy référençait needs:[ci] dans un autre workflow (inefficace sur Gitea). Déclenchement via workflow_run après CI réussie, empreinte GIT_COMMIT dans l'image, endpoint /api/build-info et health check sur 127.0.0.1:3000 avec comparaison du SHA attendu. Co-authored-by: Cursor --- .gitea/workflows/deploy.yaml | 53 +++++++++++++++++------- docker-compose.yml | 2 + memento-note/Dockerfile | 3 ++ memento-note/app/api/build-info/route.ts | 10 +++++ 4 files changed, 53 insertions(+), 15 deletions(-) create mode 100644 memento-note/app/api/build-info/route.ts diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml index b9c1982..546407c 100644 --- a/.gitea/workflows/deploy.yaml +++ b/.gitea/workflows/deploy.yaml @@ -1,17 +1,29 @@ name: Deploy to Production +# Deploy only after CI succeeds (needs: [ci] does NOT work across workflow files). on: - push: - branches: - - main + workflow_run: + workflows: ["CI"] + types: [completed] + branches: [main] workflow_dispatch: jobs: deploy: name: Build and Deploy runs-on: ubuntu-24.04 - needs: [ci] + if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} steps: + - name: Resolve target commit + id: target + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + SHA="${{ github.sha }}" + else + SHA="${{ github.event.workflow_run.head_sha }}" + fi + echo "sha=$SHA" >> "$GITHUB_OUTPUT" + echo "Deploying commit: $SHA" - name: Setup SSH run: | mkdir -p ~/.ssh @@ -152,34 +164,45 @@ jobs: echo "Backup saved: $DUMP_FILE ($(( DUMP_SIZE / 1024 ))KB)" echo "=== Building app images ===" + export GIT_COMMIT="$(git rev-parse HEAD)" + echo "GIT_COMMIT=$GIT_COMMIT" + export COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose build memento-note docker compose build mcp-server echo "=== Starting app containers with new images ===" docker compose up -d --remove-orphans --force-recreate memento-note mcp-server docker compose ps - echo "=== Deployed commit ===" + echo "=== Deployed commit (git) ===" git log -1 --oneline docker inspect memento-web --format 'Image: {{.Image}} Created: {{.Created}}' + + echo "=== Reload reverse proxy (if nginx on host) ===" + nginx -t 2>/dev/null && systemctl reload nginx 2>/dev/null || true ENDSSH - - name: Wait for app to be healthy + - name: Verify deployed commit on app (port 3000) id: health-check + env: + EXPECTED_SHA: ${{ steps.target.outputs.sha }} run: | - echo "Waiting up to 180s for http://192.168.1.190 ..." + echo "Expected commit: $EXPECTED_SHA" for i in $(seq 1 36); do - CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -L http://192.168.1.190/ || echo "000") - if [ "$CODE" != "000" ] && [ "$CODE" -lt 500 ]; then - echo "App OK (HTTP $CODE) after $((i * 5))s" + BODY=$(ssh root@192.168.1.190 "curl -sf --max-time 5 http://127.0.0.1:3000/api/build-info" 2>/dev/null || true) + ACTUAL=$(echo "$BODY" | jq -r '.commit // empty' 2>/dev/null || true) + echo " [$((i * 5))s] build-info commit=$ACTUAL" + if [ -n "$ACTUAL" ] && [ "$ACTUAL" = "$EXPECTED_SHA" ]; then + echo "Deploy verified: container serves commit $ACTUAL" echo "healthy=true" >> $GITHUB_OUTPUT exit 0 fi - echo " [$((i * 5))s] HTTP $CODE" sleep 5 done echo "healthy=false" >> $GITHUB_OUTPUT - echo "Timeout! Derniers logs :" - ssh root@192.168.1.190 "docker logs memento-web --tail=50" + echo "ERROR: App on :3000 does not serve expected commit." + echo " expected=$EXPECTED_SHA" + echo " got=$ACTUAL" + ssh root@192.168.1.190 "cd /opt/memento && git log -1 --oneline && docker logs memento-web --tail=40" exit 0 - name: Rollback on failure @@ -197,9 +220,9 @@ jobs: run: | HEALTHY="${{ steps.health-check.outputs.healthy }}" if [ "$HEALTHY" = "true" ]; then - MSG="✅ Memento deploy SUCCESS%nBranch: main%nCommit: ${{ github.sha }}" + MSG="✅ Memento deploy SUCCESS%nCommit: ${{ steps.target.outputs.sha }}" else - MSG="❌ Memento deploy FAILED%nBranch: main%nCommit: ${{ github.sha }}%nAction: rollback to previous image" + MSG="❌ Memento deploy FAILED%nCommit: ${{ steps.target.outputs.sha }}%nAction: rollback to previous image" fi curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \ -d chat_id="${{ secrets.TELEGRAM_CHAT_ID }}" \ diff --git a/docker-compose.yml b/docker-compose.yml index ca4e12a..5a00923 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,6 +29,8 @@ services: build: context: ./memento-note dockerfile: Dockerfile + args: + GIT_COMMIT: ${GIT_COMMIT:-unknown} container_name: memento-web env_file: - .env.docker diff --git a/memento-note/Dockerfile b/memento-note/Dockerfile index 0ea236e..ee65bb6 100644 --- a/memento-note/Dockerfile +++ b/memento-note/Dockerfile @@ -18,6 +18,7 @@ RUN npx prisma generate # Stage 2: Build # =========================================================================== FROM node:22-bookworm-slim AS builder +ARG GIT_COMMIT=unknown WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -38,10 +39,12 @@ RUN npm run build # Stage 3: Runner # =========================================================================== FROM node:22-bookworm-slim AS runner +ARG GIT_COMMIT=unknown WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 +ENV GIT_COMMIT=$GIT_COMMIT RUN apt-get update && apt-get install -y --no-install-recommends \ openssl \ diff --git a/memento-note/app/api/build-info/route.ts b/memento-note/app/api/build-info/route.ts new file mode 100644 index 0000000..86c1e2e --- /dev/null +++ b/memento-note/app/api/build-info/route.ts @@ -0,0 +1,10 @@ +import { NextResponse } from 'next/server' + +export const dynamic = 'force-dynamic' + +/** Public build fingerprint for deploy verification (no secrets). */ +export async function GET() { + return NextResponse.json({ + commit: process.env.GIT_COMMIT ?? 'unknown', + }) +}