fix: repair gitea deploy pipeline
This commit is contained in:
Executable
+129
@@ -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" <<EOF_ENV
|
||||
POSTGRES_DB=nexus
|
||||
POSTGRES_USER=nexus
|
||||
POSTGRES_PASSWORD=${ENV_POSTGRES_PASSWORD}
|
||||
JWT_KEY=${ENV_JWT_KEY}
|
||||
JWT_ISSUER=nexus
|
||||
JWT_AUDIENCE=nexus-web
|
||||
BOOTSTRAP_OWNER_EMAIL=vmbao62@hotmail.de
|
||||
OPENCLAW_BASE_URL=http://host.docker.internal:18789
|
||||
OPENCLAW_GATEWAY_TOKEN=${ENV_OPENCLAW_TOKEN:-}
|
||||
OPENCLAW_GATEWAY_PASSWORD=
|
||||
EOF_ENV
|
||||
|
||||
echo "Syncing source to deploy path: $DEPLOY_PATH"
|
||||
docker run --rm \
|
||||
-v "$PWD:/src:ro" \
|
||||
-v "$DEPLOY_PATH:/dest" \
|
||||
alpine:3.20 \
|
||||
sh -c '
|
||||
set -eu
|
||||
cd /src
|
||||
find . -mindepth 1 -maxdepth 1 ! -name .git -exec cp -r {} /dest/ \;
|
||||
dest_owner="$(stat -c "%u:%g" /dest)"
|
||||
chown -R "$dest_owner" /dest
|
||||
'
|
||||
|
||||
echo "Building and starting Docker compose stack"
|
||||
cat > "$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"
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}" <<EOF
|
||||
# Nexus Production Environment — auto-generated by CD pipeline
|
||||
# Managed via Gitea Secrets → do NOT edit manually.
|
||||
# This file lives in /tmp and is removed after deploy completes.
|
||||
POSTGRES_DB=nexus
|
||||
POSTGRES_USER=nexus
|
||||
POSTGRES_PASSWORD=${ENV_POSTGRES_PASSWORD}
|
||||
JWT_KEY=${ENV_JWT_KEY}
|
||||
JWT_ISSUER=nexus
|
||||
JWT_AUDIENCE=nexus-web
|
||||
BOOTSTRAP_OWNER_EMAIL=vmbao62@hotmail.de
|
||||
OPENCLAW_BASE_URL=http://host.docker.internal:18789
|
||||
OPENCLAW_GATEWAY_TOKEN=${ENV_OPENCLAW_TOKEN}
|
||||
OPENCLAW_GATEWAY_PASSWORD=
|
||||
EOF
|
||||
|
||||
chmod 600 "${ENV_TMPFILE}"
|
||||
echo "✅ .env written to ${ENV_TMPFILE} (mode 600)"
|
||||
|
||||
# ═══════════════════════════════════════════════════
|
||||
# Step 5: Sync code to host (without .env in workspace)
|
||||
# ═══════════════════════════════════════════════════
|
||||
- name: Sync code to host
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
docker run --rm \
|
||||
-v "${{ gitea.workspace }}:/src:ro" \
|
||||
-v "${DEPLOY_PATH}:/dest" \
|
||||
alpine:latest \
|
||||
sh -c "
|
||||
cd /src && \
|
||||
find . -mindepth 1 -maxdepth 1 \
|
||||
! -name .git \
|
||||
-exec cp -r {} /dest/ \; && \
|
||||
DEST_OWNER=\$(stat -c '%u:%g' /dest) && \
|
||||
chown -R \"\$DEST_OWNER\" /dest
|
||||
"
|
||||
|
||||
echo "✅ Code synced to ${DEPLOY_PATH}"
|
||||
|
||||
# ═══════════════════════════════════════════════════
|
||||
# Step 6: Build & Deploy
|
||||
#
|
||||
# The temp .env file is bind-mounted read-only into the
|
||||
# docker:cli container so compose can resolve variables.
|
||||
# It is NEVER written into the workspace directory.
|
||||
# ═══════════════════════════════════════════════════
|
||||
- name: Build & Deploy
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
BUILD_ARGS=""
|
||||
SERVICE_ARG=""
|
||||
|
||||
# Write the deploy script to a file to avoid nested quoting issues
|
||||
cat > /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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+7
-1
@@ -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.
|
||||
|
||||
+18
-17
@@ -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`)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user