diff --git a/.gitea/scripts/deploy-nexus.sh b/.gitea/scripts/deploy-nexus.sh new file mode 100755 index 0000000..12a8e62 --- /dev/null +++ b/.gitea/scripts/deploy-nexus.sh @@ -0,0 +1,129 @@ +#!/bin/sh +set -eu + +DEPLOY_PATH="${DEPLOY_PATH:-/home/projekte_bao/openclaw/data/openclaw/workspace/nexus}" +ENV_TMPFILE="${ENV_TMPFILE:-/tmp/nexus-deploy-env}" +COMPOSE_SCRIPT="${COMPOSE_SCRIPT:-/tmp/nexus-compose-deploy.sh}" +BASE_URL="${BASE_URL:-https://nexus.noveria.net}" + +cleanup() { + if [ -f "$ENV_TMPFILE" ]; then + shred -u "$ENV_TMPFILE" 2>/dev/null || rm -f "$ENV_TMPFILE" + fi + rm -f "$COMPOSE_SCRIPT" +} +trap cleanup EXIT INT TERM + +require_env() { + name="$1" + eval "value=\${$name:-}" + if [ -z "$value" ]; then + echo "Missing required environment variable: $name" >&2 + exit 1 + fi +} + +require_env ENV_POSTGRES_PASSWORD +require_env ENV_JWT_KEY + +if [ ! -f VERSION ]; then + echo "VERSION file not found" >&2 + exit 1 +fi + +VERSION="$(tr -d '[:space:]' < VERSION)" +if ! echo "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "Invalid VERSION value: $VERSION" >&2 + exit 1 +fi + +GIT_REF="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)" +echo "Deploying Nexus v$VERSION from $GIT_REF" + +umask 077 +cat > "$ENV_TMPFILE" < "$COMPOSE_SCRIPT" <<'EOF_DEPLOY' +#!/bin/sh +set -eu +cat > /tmp/nexus-deploy-env +docker compose --env-file /tmp/nexus-deploy-env build +docker compose --env-file /tmp/nexus-deploy-env up -d --force-recreate --remove-orphans --wait +docker compose --env-file /tmp/nexus-deploy-env ps +rm -f /tmp/nexus-deploy-env +EOF_DEPLOY + +docker run --rm \ + -v "$DEPLOY_PATH:/workspace/nexus" \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v "$COMPOSE_SCRIPT:/deploy.sh:ro" \ + -w /workspace/nexus \ + -i \ + docker:cli \ + sh /deploy.sh < "$ENV_TMPFILE" + +echo "Checking live health" +retry=0 +while [ "$retry" -lt 6 ]; do + retry=$((retry + 1)) + if curl -fsS --max-time 10 "$BASE_URL/health" >/dev/null; then + echo "Health check passed" + break + fi + if [ "$retry" -eq 6 ]; then + echo "Health check failed" >&2 + exit 1 + fi + sleep "$retry" +done + +pass=0 +fail=0 +check() { + path="$1" + expected="$2" + label="$3" + code="$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 "$BASE_URL$path")" + printf '%-28s HTTP %s\n' "$label" "$code" + if [ "$code" = "$expected" ]; then + pass=$((pass + 1)) + else + fail=$((fail + 1)) + fi +} + +check "/dashboard" "200" "Dashboard" +check "/health" "200" "Health" +check "/api/v1/operations/snapshot" "401" "Operations auth" + +if [ "$fail" -ne 0 ]; then + echo "Smoke test failed: $fail failed, $pass passed" >&2 + exit 1 +fi + +echo "Nexus v$VERSION deployed and verified" diff --git a/.gitea/workflows/backup.yaml b/.gitea/workflows/backup.yaml index 0d87b40..5edb30d 100644 --- a/.gitea/workflows/backup.yaml +++ b/.gitea/workflows/backup.yaml @@ -43,7 +43,7 @@ on: jobs: backup: name: Backup PostgreSQL - runs-on: ubuntu-latest + runs-on: linux env: ENV_TMPFILE: /tmp/nexus-backup-env ENV_POSTGRES_PASSWORD: ${{ secrets.ENV_POSTGRES_PASSWORD }} diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 442c3d1..a93aa6d 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -97,3 +97,25 @@ jobs: else echo "βœ… No obvious secrets found" fi + + deploy: + name: Deploy Nexus + runs-on: linux + needs: [backend, frontend, security] + concurrency: + group: deploy-production + cancel-in-progress: false + if: | + github.event_name == 'push' && + github.ref == 'refs/heads/main' + env: + DEPLOY_PATH: /home/projekte_bao/openclaw/data/openclaw/workspace/nexus + ENV_POSTGRES_PASSWORD: ${{ secrets.ENV_POSTGRES_PASSWORD }} + ENV_JWT_KEY: ${{ secrets.ENV_JWT_KEY }} + ENV_OPENCLAW_TOKEN: ${{ secrets.ENV_OPENCLAW_TOKEN }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Deploy after green CI + run: sh .gitea/scripts/deploy-nexus.sh diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml index 9b77509..3f27a19 100644 --- a/.gitea/workflows/deploy.yaml +++ b/.gitea/workflows/deploy.yaml @@ -1,364 +1,28 @@ -name: Deploy Nexus v2 -run-name: πŸš€ Deploy v2 by @${{ gitea.actor }} +name: Deploy Nexus Manual +run-name: Deploy Nexus manually by @${{ gitea.actor }} -# ─────────────────────────────────────────────────────── -# Owner: DevOps (Architekt) -# CD v3 β€” 2026-06-13 -# -# Triggers: -# 1. AUTOMATIC after successful CI on main (workflow_run) -# β†’ Deploys main with the VERSION already present in the repo. -# β†’ Commits marked with [skip ci] are filtered at job level -# (prevents version-bump loops). -# 2. MANUAL via workflow_dispatch with full parameter control. -# -# Concurrency: one deploy at a time. -# Queued deploys wait β€” no race conditions with parallel builds. -# -# Version Management: -# The VERSION file in the repo root is the single source of truth. -# Deploy only reads, validates, and logs the version. -# Version changes happen before merge to main, not during deploy. -# ─────────────────────────────────────────────────────── concurrency: group: deploy-production cancel-in-progress: false on: - # ── Auto-Trigger: after successful CI on main ── - workflow_run: - workflows: ["CI - Build & Test"] - types: [completed] - branches: [main] - - # ── Manual Trigger (full control) ── workflow_dispatch: jobs: deploy: name: Deploy Nexus - runs-on: ubuntu-latest - if: | - (github.event_name == 'workflow_dispatch') || - (github.event_name == 'workflow_run' && - github.event.workflow_run.conclusion == 'success' && - !contains(github.event.workflow_run.head_commit.message, '[skip ci]')) - - # ── Env for the deploy target path ── + runs-on: linux env: DEPLOY_PATH: /home/projekte_bao/openclaw/data/openclaw/workspace/nexus - ENV_TMPFILE: /tmp/nexus-deploy-env ENV_POSTGRES_PASSWORD: ${{ secrets.ENV_POSTGRES_PASSWORD }} ENV_JWT_KEY: ${{ secrets.ENV_JWT_KEY }} ENV_OPENCLAW_TOKEN: ${{ secrets.ENV_OPENCLAW_TOKEN }} - # Owner password is not injected at deploy time. - # After first seed, the database is the only password source. - steps: - # ═══════════════════════════════════════════════════ - # Step 1: Checkout - # ═══════════════════════════════════════════════════ - - name: Checkout + - name: Checkout main uses: actions/checkout@v4 with: ref: main fetch-depth: 0 - fetch-tags: true - # ═══════════════════════════════════════════════════ - # Step 2: Set up Git identity - # ═══════════════════════════════════════════════════ - - name: Configure Git - run: | - git config user.email "devops@noveria.net" - git config user.name "DevOps" - - # ═══════════════════════════════════════════════════ - # Step 3: Resolve deploy version - # - # Reads VERSION from repo root β€” the single source of truth. - # Validates semver format, logs version + git metadata. - # No git mutation: version bumps happen in the Dev workflow. - # ═══════════════════════════════════════════════════ - - name: Resolve Version - id: version - run: | - set -euo pipefail - - # 1. Check VERSION exists - if [ ! -f VERSION ]; then - echo "❌ VERSION file not found" - exit 1 - fi - - # 2. Read and validate semver format - VERSION=$(cat VERSION | tr -d '[:space:]') - if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then - echo "❌ Invalid semver in VERSION: '$VERSION'" - exit 1 - fi - - # 3. Log version, git ref, and describe - GIT_REF=$(git rev-parse --short HEAD) - GIT_DESCRIBE=$(git describe --always --dirty) - - echo "πŸ“¦ Deploy version: v${VERSION}" - echo "πŸ”– Git ref: ${GIT_REF}" - echo "🏷️ Git describe: ${GIT_DESCRIBE}" - - # 4. Set outputs for downstream steps - echo "version=${VERSION}" >> "$GITEA_OUTPUT" - echo "mutated_main=false" >> "$GITEA_OUTPUT" - - # ═══════════════════════════════════════════════════ - # Step 4: Build .env from secrets (SAFE) - # - # Secrets are written to /tmp/nexus-deploy-env β€” NEVER - # to a file inside the workspace that gets rsync'd to - # the host. The temp file is deleted immediately after - # compose operations complete. - # - # Owner password is deliberately omitted so production deploys - # cannot overwrite the persisted DB password. - # Other secrets (POSTGRES_PASSWORD, JWT_KEY, OPENCLAW_TOKEN) - # come from Gitea secrets. - # ═══════════════════════════════════════════════════ - - name: Prepare .env (secrets β†’ temp file) - run: | - set -euo pipefail - - cat > "${ENV_TMPFILE}" < /tmp/nexus-deploy-script.sh << 'DEPLOYSCRIPT' -#!/bin/sh -set -e -trap 'rm -f /tmp/nexus-deploy-env' EXIT -cat > /tmp/nexus-deploy-env - -# ── Graceful shutdown (preserves DB volume integrity) ── -docker compose --env-file /tmp/nexus-deploy-env stop postgres 2>/dev/null || true -docker compose --env-file /tmp/nexus-deploy-env down --remove-orphans 2>/dev/null || true -echo "Postgres volume preserved (nexus-postgres) β€” no WAL reset" - -BUILD_ARGS="${DEPLOY_BUILD_ARGS:-}" -SERVICE="${DEPLOY_SERVICE:-}" - -if [ -n "$SERVICE" ]; then - echo "Deploying service: $SERVICE" - docker compose --env-file /tmp/nexus-deploy-env build $BUILD_ARGS $SERVICE - docker compose --env-file /tmp/nexus-deploy-env up -d --force-recreate $SERVICE -else - echo 'Deploying all services' - docker compose --env-file /tmp/nexus-deploy-env build $BUILD_ARGS - docker compose --env-file /tmp/nexus-deploy-env up -d --force-recreate -fi - -echo 'Waiting for services to become healthy (up to 180s)...' -for i in $(seq 1 36); do - STATUS=$(docker compose --env-file /tmp/nexus-deploy-env ps -a 2>/dev/null | tail -n +2) - if echo "$STATUS" | grep -q 'unhealthy'; then - echo " [$i/36] Unhealthy containers - failing fast" - docker compose --env-file /tmp/nexus-deploy-env ps -a - docker compose --env-file /tmp/nexus-deploy-env logs --tail=30 - exit 1 - elif echo "$STATUS" | grep -q 'starting'; then - echo " [$i/36] Still starting..." - sleep 5 - else - echo 'All containers healthy' - docker compose --env-file /tmp/nexus-deploy-env ps -a - exit 0 - fi -done -echo 'Timeout waiting for services' -docker compose --env-file /tmp/nexus-deploy-env ps -a -docker compose --env-file /tmp/nexus-deploy-env logs --tail=20 -exit 1 -DEPLOYSCRIPT - - docker run --rm \ - -e "DEPLOY_BUILD_ARGS=${BUILD_ARGS:-}" \ - -e "DEPLOY_SERVICE=${SERVICE_ARG:-}" \ - -v "${DEPLOY_PATH}:/workspace/nexus" \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v /tmp/nexus-deploy-script.sh:/deploy.sh:ro \ - -w /workspace/nexus \ - -i \ - docker:cli \ - sh /deploy.sh < "${ENV_TMPFILE}" - - rm -f /tmp/nexus-deploy-script.sh - - echo "βœ… Docker compose up completed" - - # ═══════════════════════════════════════════════════ - # Step 7: Clean up temp .env - # ═══════════════════════════════════════════════════ - - name: Clean up temp .env - if: always() - run: | - if [ -f "${ENV_TMPFILE}" ]; then - shred -u "${ENV_TMPFILE}" 2>/dev/null || rm -f "${ENV_TMPFILE}" - echo "🧹 Temp .env removed" - fi - - # ═══════════════════════════════════════════════════ - # Step 8: Health Check (exponential backoff) - # ═══════════════════════════════════════════════════ - - name: Health Check - run: | - echo "πŸ₯ Health check..." - RETRY=0 - MAX=6 - WAIT=1 - while [ $RETRY -lt $MAX ]; do - RETRY=$((RETRY + 1)) - if curl -sf --max-time 10 https://nexus.noveria.net/health; then - echo "" - echo "βœ… Health check passed (attempt $RETRY/$MAX)" - exit 0 - fi - echo "⏳ Attempt $RETRY/$MAX failed, waiting ${WAIT}s..." - sleep $WAIT - # Fibonacci-ish backoff: 1,2,3,5,8,13 - NEXT=$((WAIT + RETRY)) - [ $NEXT -le 15 ] && WAIT=$NEXT || WAIT=15 - done - echo "❌ Health check failed after $MAX attempts" - exit 1 - - # ═══════════════════════════════════════════════════ - # Step 9: Smoke Test - # ═══════════════════════════════════════════════════ - - name: Smoke Test - run: | - echo "πŸ” Smoke test..." - PASS=0 - FAIL=0 - BASE="https://nexus.noveria.net" - - check() { - local path="$1" label="$2" expected="${3:-200}" - local code - code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "${BASE}${path}") - printf " %-25s HTTP %s" "${label}:" "${code}" - if [ "$code" = "$expected" ]; then - echo " βœ…" - PASS=$((PASS + 1)) - else - echo " ❌ (expected $expected)" - FAIL=$((FAIL + 1)) - fi - } - - check "/dashboard" "Dashboard" 200 - check "/health" "Health API" 200 - check "/api/v1/operations/snapshot" "Operations API (auth)" 401 - - echo "" - echo "Results: $PASS passed, $FAIL failed" - if [ "$FAIL" -gt 0 ]; then - echo "❌ Smoke test failed!" - exit 1 - fi - echo "βœ… Smoke test passed β€” v${{ steps.version.outputs.version }} is live" - - # ═══════════════════════════════════════════════════ - # Step 10: Deployment Summary - # ═══════════════════════════════════════════════════ - - name: Deployment Summary - if: always() - run: | - TRIGGER="${{ github.event_name == 'workflow_run' && 'Auto (CI success)' || 'Manual (workflow_dispatch)' }}" - echo "" - echo "═══════════════════════════════════════" - echo " πŸ“¦ Deploy Summary" - echo "═══════════════════════════════════════" - echo " Version: v${{ steps.version.outputs.version }}" - echo " Git ref: main" - echo " Service: all" - echo " Trigger: ${TRIGGER}" - echo " Actor: @${{ gitea.actor }}" - echo " Status: ${{ job.status }}" - echo "═══════════════════════════════════════" - - # ═══════════════════════════════════════════════════ - # Step 11: Failure β†’ Reviewer Handoff - # - # On failure: DevOps (Architekt) analyses the log, - # notifies Reviewer (Code-Fixer) with the exact error. - # This output provides a ready-to-copy message. - # ═══════════════════════════════════════════════════ - - name: πŸ”΄ Failure β€” Reviewer Handoff - if: failure() - run: | - echo "" - echo "β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”" - echo "β”‚ πŸ”΄ DEPLOY FAILED β€” Reviewer muss fixen β”‚" - echo "β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€" - echo "β”‚ β”‚" - echo "β”‚ Version: v${{ steps.version.outputs.version }}" - echo "β”‚ Job: ${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_id }}" - echo "β”‚ β”‚" - echo "β”‚ β†’ DevOps (Architekt) analysiert den Fehler β”‚" - echo "β”‚ β†’ Reviewer (Code-Fixer) behebt das Problem β”‚" - echo "β”‚ β†’ DevOps verifiziert mit neuem Deploy β”‚" - echo "β”‚ β”‚" - echo "β”‚ Rollback: Trigger 'Rollback to Previous Version' β”‚" - echo "β”‚ workflow manuell in Gitea Actions. β”‚" - echo "β”‚ β”‚" - echo "β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜" + - name: Deploy main + run: sh .gitea/scripts/deploy-nexus.sh diff --git a/.gitea/workflows/rollback.yaml b/.gitea/workflows/rollback.yaml index c591f00..da36ff5 100644 --- a/.gitea/workflows/rollback.yaml +++ b/.gitea/workflows/rollback.yaml @@ -40,7 +40,7 @@ on: jobs: rollback: name: Rollback Nexus - runs-on: ubuntu-latest + runs-on: linux env: DEPLOY_PATH: /home/projekte_bao/openclaw/data/openclaw/workspace/nexus ENV_TMPFILE: /tmp/nexus-rollback-env diff --git a/README.md b/README.md index 4a00fc5..a2086ac 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ adapter-backed agent runtime, not a dependency of the frontend or domain model. > Backend-BrΓΌcke und Gateway-Integration geprΓΌft. Siehe > [`docs/architecture-board-first-orchestration.md`](docs/architecture-board-first-orchestration.md) -> CI runs automatically on every push. CD can run **automatically after successful CI** +> CI runs automatically on every push. CD runs **inside the green CI run** > on main or can be triggered **manually** (workflow_dispatch). Deploy reads > `VERSION` but does not mutate Git or create tags. Rollback and database backup > are separate manual workflows. @@ -351,20 +351,21 @@ Every push to `main` triggers `.gitea/workflows/ci.yaml`: CI must never break. If it does, Reviewer fixes. -### CD β€” Auto + Manual (CD v3) +### CD β€” Auto + Manual (CD v4) Deployment can happen automatically or manually: -#### Auto-Deploy (after successful CI on main) +#### Auto-Deploy (after successful CI jobs on main) -- Triggered by `workflow_run` after `CI - Build & Test` succeeds on `main` +- Runs as the final `Deploy Nexus` job in `.gitea/workflows/ci.yaml` +- Starts only after backend, frontend, and security jobs succeed on `main` - Deploys the current `main` version after CI succeeds. -- Skips automatically if the triggering commit contains `[skip ci]` -- The deploy workflow reads `VERSION`; it does not mutate Git, bump versions, or create tags +- This replaces `workflow_run`, which did not create deploy runs in this Gitea 1.26.3 installation. +- The deploy script reads `VERSION`; it does not mutate Git, bump versions, or create tags #### Manual Deploy (`workflow_dispatch`) -1. DevOps triggers `Deploy Nexus v2` in Gitea Actions +1. DevOps triggers `Deploy Nexus Manual` in Gitea Actions 2. Workflow validates `VERSION`, builds and deploys `main` 3. Health check + smoke test verify the deployment diff --git a/phases/changelog.md b/phases/changelog.md index d8ab765..c42c63c 100644 --- a/phases/changelog.md +++ b/phases/changelog.md @@ -1,6 +1,12 @@ # Changelog -> Letzte Aktualisierung: 2026-06-21 +> Letzte Aktualisierung: 2026-06-24 + +- 2026-06-24: **Gitea CI/CD auf CD v4 repariert.** + - Root Cause: Gitea 1.26.3 erzeugte nach grΓΌnen CI-Runs 303/304 keinen `workflow_run`-Deploy; `ubuntu-latest` passte zudem nicht zum belegbar funktionierenden Runner-Label `linux`. + - Fix: Auto-Deploy lΓ€uft jetzt als `needs`-Job in `ci.yaml` nach Backend, Frontend und Security. `deploy.yaml` ist ein kleiner manueller Fallback via `workflow_dispatch`. + - Deploy-Logik wurde in `.gitea/scripts/deploy-nexus.sh` zusammengefΓΌhrt. Secrets bleiben in `/tmp`, es wird kein Owner-Passwort injiziert, und Deploy mutiert Git-History nicht. + - Rollback- und Backup-Workflow nutzen ebenfalls das Runner-Label `linux`. - 2026-06-21: **Permanenter Owner-Passwort-Persistenz-Fix (SeedAudit + Single Source of Truth).** - Root Cause: Passwort-Injektion ΓΌber Deploy-Runtime erzeugte einen unnΓΆtigen zweiten Pfad neben der DB und verursachte Drift nach DB-Reseed. diff --git a/phases/deployment.md b/phases/deployment.md index 13509f5..23fa326 100644 --- a/phases/deployment.md +++ b/phases/deployment.md @@ -1,45 +1,46 @@ # Deployment -> Letzte Aktualisierung: 2026-06-21 -> Status: βœ… CD v3 (Auto + Manual) + Owner-Passwort-Persistenz (SeedAudit) +> Letzte Aktualisierung: 2026-06-24 +> Status: βœ… CD v4 (Auto inside CI + Manual) + Owner-Passwort-Persistenz (SeedAudit) > Live-URL: https://nexus.noveria.net -## CD-Philosophie (v3) +## CD-Philosophie (v4) - **CI lΓ€uft automatisch** bei jedem Push β†’ darf nie brechen -- **CD auto + manuell**: Automatischer Deploy nach CI-Success auf main; manueller Deploy via `workflow_dispatch` -- **Loop-Schutz**: Commits mit `[skip ci]` werden von Auto-Deploys ignoriert +- **CD auto + manuell**: Automatischer Deploy als abschließender CI-Job auf main; manueller Deploy via `workflow_dispatch` +- **Loop-Schutz**: Deploy mutiert Git nicht und erzeugt deshalb keine Deploy-Schleifen - Deploy liest und validiert `VERSION`, mutiert aber weder Git noch Tags - **Rollback** als eigener Workflow, manuell triggerbar - **Database-Backup** als eigener Workflow, manuell triggerbar (optionaler Nightly-Schedule) ## Workflows -### Deploy (`.gitea/workflows/deploy.yaml`) +### Auto-Deploy (`.gitea/workflows/ci.yaml`) + +**Trigger**: +- **Automatisch**: Der Job `Deploy Nexus` lΓ€uft nach `backend`, `frontend` und `security`. +- Bedingung: Push auf `main`, alle CI-Jobs grΓΌn. +- Grund: Gitea 1.26.3 hat fΓΌr grΓΌne CI-Runs 303/304 keinen `workflow_run`-Deploy erzeugt; der zuverlΓ€ssigste Ersatz ist ein `needs`-gesteuerter Job im selben Workflow. + +### Manual Deploy (`.gitea/workflows/deploy.yaml`) **Trigger**: -- **Automatisch**: Nach erfolgreicher CI (`workflow_run` auf `CI - Build & Test`) - β†’ Deployt `main` mit dem im Repo gesetzten `VERSION`-Wert - **Manuell**: Via Gitea Actions β†’ `workflow_dispatch` **Loop-Schutz**: -- Version-Bump-Commits enthalten `[skip ci]` β†’ Gitea startet keine neue CI -- Auto-Deploy prΓΌft zusΓ€tzlich `github.event.workflow_run.head_commit.message` auf `[skip ci]` -- Beide Mechanismen zusammen verhindern Endlosschleife: CI β†’ Deploy β†’ Bump β†’ CI … +- Deploy erstellt keine Commits, Tags oder Version-Bumps. Dadurch entsteht keine CI/CD-Schleife. **Inputs**: keine. Der manuelle Deploy nutzt denselben Main-Deploy-Pfad wie der Auto-Deploy. **Ablauf**: -1. Job-Level-Guard: Auto-Deploys fuer `[skip ci]`-Commits werden gar nicht gestartet -2. Checkout von `main` +1. Checkout von `main` (beim manuellen Deploy) oder Nutzung des geprΓΌften CI-Checkouts (Auto-Deploy) +2. `.gitea/scripts/deploy-nexus.sh` 3. `VERSION` lesen und SemVer validieren 4. **Safe Secret Handling**: `.env` wird aus Secret-Umgebungsvariablen in `/tmp/nexus-deploy-env` geschrieben (mode 600), **NICHT** im Workspace 5. Code-Sync zum Host-Deploy-Pfad -6. `docker compose build && up -d --force-recreate` +6. `docker compose build && up -d --force-recreate --remove-orphans --wait` 7. `.env`-Tempfile wird mit `shred` gelΓΆscht -8. Health-Check (Backoff, 6 Versuche) -9. Smoke-Test (`/dashboard`, `/health`, `/api/v1/operations/snapshot` erwartet `401`) -10. Bei Fehler: Reviewer-Handoff-Meldung mit Job-URL +8. Health-Check und Smoke-Test (`/dashboard`, `/health`, `/api/v1/operations/snapshot` erwartet `401`) ### Backup (`.gitea/workflows/backup.yaml`)