chore: simplify nexus cicd pipeline

This commit is contained in:
2026-06-23 18:39:38 +02:00
parent 195c497c88
commit 1214cf9a4d
15 changed files with 49 additions and 606 deletions
-10
View File
@@ -1,10 +0,0 @@
POSTGRES_DB=nexus
POSTGRES_USER=nexus
POSTGRES_PASSWORD=replace-with-a-strong-database-password
JWT_KEY=replace-with-at-least-32-random-bytes
BOOTSTRAP_OWNER_EMAIL=owner@example.com
OPENCLAW_BASE_URL=http://host.docker.internal:18789
OPENCLAW_GATEWAY_TOKEN=
OPENCLAW_GATEWAY_PASSWORD=
OLLAMA_BASE_URL=http://host.docker.internal:11434
NVIDIA_API_KEY=
+1 -1
View File
@@ -72,7 +72,7 @@ jobs:
echo "🗄️ Dumping PostgreSQL cluster..."
docker exec "${BACKUP_CONTAINER_NAME}" \
sh -c "PGPASSWORD='${ENV_POSTGRES_PASSWORD}' pg_dumpall -U nexus -h localhost" \
sh -c "PGPASSWORD='${ENV_POSTGRES_PASSWORD}' pg_dumpall -U nexus" \
| gzip > "${{ steps.meta.outputs.filename }}"
SIZE=$(du -h "${{ steps.meta.outputs.filename }}" | cut -f1)
+1 -1
View File
@@ -51,7 +51,7 @@ jobs:
- name: Setup pnpm
run: |
corepack enable
corepack prepare pnpm@latest --activate
corepack prepare pnpm@10.12.1 --activate
- name: Install dependencies
run: pnpm install --frozen-lockfile
-186
View File
@@ -1,186 +0,0 @@
name: Deploy Now
run-name: 🚀 Deploy Now by @${{ gitea.actor }}
on:
workflow_dispatch:
jobs:
deploy:
name: Deploy Nexus
runs-on: ubuntu-latest
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 }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
fetch-tags: true
- name: Resolve Version
id: version
run: |
set -euo pipefail
if [ ! -f VERSION ]; then
echo "ERROR: VERSION file not found"
exit 1
fi
VERSION=$(cat VERSION | tr -d '[:space:]')
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "ERROR: Invalid semver in VERSION: $VERSION"
exit 1
fi
GIT_REF=$(git rev-parse --short HEAD)
echo "Deploy version: v${VERSION} git:${GIT_REF}"
echo "version=${VERSION}" >> "$GITEA_OUTPUT"
- name: Prepare .env
run: |
set -euo pipefail
printf 'POSTGRES_DB=nexus\n' > "${ENV_TMPFILE}"
printf 'POSTGRES_USER=nexus\n' >> "${ENV_TMPFILE}"
printf 'POSTGRES_PASSWORD=%s\n' "${ENV_POSTGRES_PASSWORD}" >> "${ENV_TMPFILE}"
printf 'JWT_KEY=%s\n' "${ENV_JWT_KEY}" >> "${ENV_TMPFILE}"
printf 'JWT_ISSUER=nexus\n' >> "${ENV_TMPFILE}"
printf 'JWT_AUDIENCE=nexus-web\n' >> "${ENV_TMPFILE}"
printf 'BOOTSTRAP_OWNER_EMAIL=vmbao62@hotmail.de\n' >> "${ENV_TMPFILE}"
printf 'OPENCLAW_BASE_URL=http://host.docker.internal:18789\n' >> "${ENV_TMPFILE}"
printf 'OPENCLAW_GATEWAY_TOKEN=%s\n' "${ENV_OPENCLAW_TOKEN}" >> "${ENV_TMPFILE}"
printf 'OPENCLAW_GATEWAY_PASSWORD=\n' >> "${ENV_TMPFILE}"
chmod 600 "${ENV_TMPFILE}"
echo "OK .env written to ${ENV_TMPFILE}"
- 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 "OK synced to ${DEPLOY_PATH}"
- name: Build and Deploy
run: |
set -euo pipefail
SCRIPT=/tmp/nexus-deploy-script.sh
printf '#!/bin/sh\n' > "$SCRIPT"
printf 'set -e\n' >> "$SCRIPT"
printf 'trap "rm -f /tmp/nexus-deploy-env" EXIT\n' >> "$SCRIPT"
printf 'cat > /tmp/nexus-deploy-env\n' >> "$SCRIPT"
printf '\n' >> "$SCRIPT"
printf '# ── Graceful shutdown (preserves DB volume integrity) ──\n' >> "$SCRIPT"
printf 'docker compose --env-file /tmp/nexus-deploy-env stop postgres 2>/dev/null || true\n' >> "$SCRIPT"
printf 'docker compose --env-file /tmp/nexus-deploy-env down --remove-orphans 2>/dev/null || true\n' >> "$SCRIPT"
printf 'echo "Postgres volume preserved (nexus-postgres) — no WAL reset"\n' >> "$SCRIPT"
printf '\n' >> "$SCRIPT"
printf 'echo "Deploying all services"\n' >> "$SCRIPT"
printf 'docker compose --env-file /tmp/nexus-deploy-env build --no-cache\n' >> "$SCRIPT"
printf 'docker compose --env-file /tmp/nexus-deploy-env up -d --force-recreate\n' >> "$SCRIPT"
printf '\n' >> "$SCRIPT"
printf 'echo "Waiting for services to become healthy (up to 180s)..."\n' >> "$SCRIPT"
printf 'for i in $(seq 1 36); do\n' >> "$SCRIPT"
printf ' STATUS=$(docker compose --env-file /tmp/nexus-deploy-env ps -a 2>/dev/null | tail -n +2)\n' >> "$SCRIPT"
printf ' if echo "$STATUS" | grep -q unhealthy; then\n' >> "$SCRIPT"
printf ' echo " [$i/36] Unhealthy containers - failing fast"\n' >> "$SCRIPT"
printf ' docker compose --env-file /tmp/nexus-deploy-env ps -a\n' >> "$SCRIPT"
printf ' docker compose --env-file /tmp/nexus-deploy-env logs --tail=30\n' >> "$SCRIPT"
printf ' exit 1\n' >> "$SCRIPT"
printf ' elif echo "$STATUS" | grep -q starting; then\n' >> "$SCRIPT"
printf ' echo " [$i/36] Still starting..."\n' >> "$SCRIPT"
printf ' sleep 5\n' >> "$SCRIPT"
printf ' else\n' >> "$SCRIPT"
printf ' echo "All containers healthy"\n' >> "$SCRIPT"
printf ' docker compose --env-file /tmp/nexus-deploy-env ps -a\n' >> "$SCRIPT"
printf ' exit 0\n' >> "$SCRIPT"
printf ' fi\n' >> "$SCRIPT"
printf 'done\n' >> "$SCRIPT"
printf 'echo "Timeout waiting for services"\n' >> "$SCRIPT"
printf 'docker compose --env-file /tmp/nexus-deploy-env ps -a\n' >> "$SCRIPT"
printf 'docker compose --env-file /tmp/nexus-deploy-env logs --tail=20\n' >> "$SCRIPT"
printf 'exit 1\n' >> "$SCRIPT"
chmod +x "$SCRIPT"
docker run --rm \
-v "${DEPLOY_PATH}:/workspace/nexus" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "${SCRIPT}:/deploy.sh:ro" \
-w /workspace/nexus \
-i \
docker:cli \
sh /deploy.sh < "${ENV_TMPFILE}"
rm -f "$SCRIPT"
echo "OK deployed"
- 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 "OK cleaned"
fi
- 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 "OK Health check passed (attempt $RETRY/$MAX)"
exit 0
fi
echo "Attempt $RETRY/$MAX failed, waiting ${WAIT}s..."
sleep $WAIT
NEXT=$((WAIT + RETRY))
[ $NEXT -le 15 ] && WAIT=$NEXT || WAIT=15
done
echo "ERROR Health check failed after $MAX attempts"
exit 1
- name: Smoke Test
run: |
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 " OK"
PASS=$((PASS + 1))
else
echo " FAIL (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 "Results: $PASS passed, $FAIL failed"
if [ "$FAIL" -gt 0 ]; then
echo "ERROR Smoke test failed"
exit 1
fi
echo "OK Smoke test passed"
- name: Summary
if: always()
run: |
echo "========================================"
echo " Deploy Summary"
echo "========================================"
echo " Version: v${{ steps.version.outputs.version }}"
echo " Git ref: main"
echo " Service: all"
echo " Trigger: Manual"
echo " Status: ${{ job.status }}"
echo "========================================"
+3 -4
View File
@@ -7,7 +7,7 @@ run-name: 🚀 Deploy v2 by @${{ gitea.actor }}
#
# Triggers:
# 1. AUTOMATIC after successful CI on main (workflow_run)
# → Uses safe defaults: patch bump, all services, main ref.
# → 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.
@@ -17,9 +17,8 @@ run-name: 🚀 Deploy v2 by @${{ gitea.actor }}
#
# Version Management:
# The VERSION file in the repo root is the single source of truth.
# Version bumps happen in the Dev workflow BEFORE merge to main.
# The deploy workflow only reads, validates, and logs the version.
# The [skip ci] filter remains as a safety layer for auto-triggers.
# Deploy only reads, validates, and logs the version.
# Version changes happen before merge to main, not during deploy.
# ───────────────────────────────────────────────────────
concurrency:
group: deploy-production
-1
View File
@@ -6,7 +6,6 @@
# Environment
.env
!.env.example
!.env.template
# IDE
+10 -12
View File
@@ -8,9 +8,9 @@ adapter-backed agent runtime, not a dependency of the frontend or domain model.
> [`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**
> on main (patch-bump default) or can be triggered **manually** (workflow_dispatch) with
> full parameter control. Main deploys bump/tag a release; arbitrary `git_ref` deploys
> stay read-only. Rollback and database backup are separate manual workflows.
> 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.
> See [phases/deployment.md](phases/deployment.md) for full CD documentation.
## Current foundation
@@ -26,7 +26,7 @@ adapter-backed agent runtime, not a dependency of the frontend or domain model.
## Local/container start
```bash
cp .env.example .env
cp .env.template .env
# Replace every placeholder, especially POSTGRES_PASSWORD, JWT_KEY and BOOTSTRAP_OWNER_EMAIL.
docker compose up --build -d
curl http://127.0.0.1:18880/health
@@ -358,17 +358,15 @@ Deployment can happen automatically or manually:
#### Auto-Deploy (after successful CI on main)
- Triggered by `workflow_run` after `CI - Build & Test` succeeds on `main`
- Uses safe defaults: `patch` bump, all services, main ref
- Skips automatically if the triggering commit contains `[skip ci]` (version-bump commits)
- The version-bump commit itself uses `[skip ci]` → no infinite CI→Deploy→Bump→CI loops
- 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
#### Manual Deploy (`workflow_dispatch`)
1. DevOps triggers `Deploy to Production` in Gitea Actions (or Iris auto-approves)
2. Chooses version bump type: patch (default) / minor / major
3. Optionally scopes to a single service or specific git ref
4. Workflow bumps VERSION, creates git tag, builds and deploys
5. Health check + smoke test verify the deployment
1. DevOps triggers `Deploy Nexus v2` in Gitea Actions
2. Workflow validates `VERSION`, builds and deploys `main`
3. Health check + smoke test verify the deployment
#### Rollback (`workflow_dispatch`)
+10
View File
@@ -0,0 +1,10 @@
bin/
obj/
*.user
*.suo
.vs/
.vscode/
.git/
.gitignore
.env
*.log
+9
View File
@@ -0,0 +1,9 @@
node_modules/
dist/
.pnpm-store/
.pnpm-home/
.corepack-home/
.git/
.gitignore
.env
*.log
-70
View File
@@ -1,70 +0,0 @@
#!/bin/bash
# Nexus Deployment Script
# Auf dem VPS-HOST ausführen, nicht im Container!
set -e
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
NEXUS_DIR="$(dirname "$SCRIPT_DIR")"
echo "=== Nexus Deployment ==="
echo "Verzeichnis: $NEXUS_DIR"
cd "$NEXUS_DIR"
echo ""
echo "[1/4] Prüfe Konfiguration..."
docker compose config --quiet && echo " ✅ Konfiguration gültig"
echo ""
echo "[2/4] Starte Stack (mit Healthchecks)..."
docker compose up -d --wait
echo ""
echo "[3/4] Status nach Deployment..."
docker compose ps
echo ""
echo "[4/4] Verifikation..."
check_code() {
local path="$1"
curl -s -o /dev/null -w "%{http_code}" "http://localhost:18880${path}"
}
HEALTH_CODE=$(check_code /health)
DASHBOARD_CODE=$(check_code /dashboard)
OPS_CODE=$(check_code /api/v1/operations/snapshot)
if [ "$HEALTH_CODE" = "200" ] && [ "$DASHBOARD_CODE" != "200" ]; then
WEB_CID="$(docker compose ps -q web || true)"
if [ -n "$WEB_CID" ]; then
WEB_STATE="$(docker inspect -f '{{.State.Status}}' "$WEB_CID" 2>/dev/null || true)"
if [ "$WEB_STATE" = "created" ]; then
echo " ️ API healthy, aber web noch im Status 'created' — starte web nach"
docker compose up -d web
sleep 2
DASHBOARD_CODE=$(check_code /dashboard)
OPS_CODE=$(check_code /api/v1/operations/snapshot)
fi
fi
fi
echo " /health -> ${HEALTH_CODE}"
echo " /dashboard -> ${DASHBOARD_CODE}"
echo " /api/v1/operations/snapshot -> ${OPS_CODE}"
if [ "$HEALTH_CODE" != "200" ] || [ "$DASHBOARD_CODE" != "200" ] || [ "$OPS_CODE" != "401" ]; then
echo " ❌ Verifikation fehlgeschlagen"
exit 1
fi
echo " ✅ Health-Check bestanden"
echo " ✅ Dashboard erreichbar"
echo " ✅ Operations API fordert Auth an"
echo ""
echo "=== Deployment abgeschlossen ==="
echo "Dashboard: https://nexus.noveria.net/dashboard"
echo "Health-API: https://nexus.noveria.net/health"
echo ""
echo "Login-Informationen: docker compose logs api | grep 'Initial owner'"
echo "Status: docker compose ps"
-55
View File
@@ -1,55 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
MODEL="${OLLAMA_MODEL:-qwen3:4b}"
BIND_ADDRESS="${OLLAMA_BIND_ADDRESS:-172.18.0.1:11434}"
BACKUP_DIR="/root/security-backups/ollama-$(date -u +%Y%m%dT%H%M%SZ)"
if [[ "${EUID}" -ne 0 ]]; then
echo "Run this script as root on the Ubuntu host." >&2
exit 1
fi
mkdir -p "${BACKUP_DIR}"
if systemctl cat ollama.service >/dev/null 2>&1; then
systemctl cat ollama.service > "${BACKUP_DIR}/ollama.service.before.txt"
fi
if [[ -d /etc/systemd/system/ollama.service.d ]]; then
cp -a /etc/systemd/system/ollama.service.d "${BACKUP_DIR}/"
fi
if ! command -v ollama >/dev/null 2>&1; then
curl -fsSL https://ollama.com/install.sh -o /tmp/ollama-install.sh
sh /tmp/ollama-install.sh
fi
install -d -m 755 /etc/systemd/system/ollama.service.d
cat > /etc/systemd/system/ollama.service.d/10-openclaw.conf <<OVERRIDE
[Service]
Environment="OLLAMA_HOST=${BIND_ADDRESS}"
Environment="OLLAMA_KEEP_ALIVE=15m"
OVERRIDE
systemctl daemon-reload
systemctl enable --now ollama
systemctl restart ollama
max_attempts=30
attempt=1
while [[ "${attempt}" -le "${max_attempts}" ]]; do
if curl -fsS "http://${BIND_ADDRESS}/api/tags" >/dev/null; then
break
fi
if [[ "${attempt}" -eq "${max_attempts}" ]]; then
systemctl status ollama --no-pager
exit 1
fi
attempt=$((attempt + 1))
sleep 2
done
OLLAMA_HOST="http://${BIND_ADDRESS}" ollama pull "${MODEL}"
OLLAMA_HOST="http://${BIND_ADDRESS}" ollama show "${MODEL}" >/dev/null
curl -fsS "http://${BIND_ADDRESS}/api/tags"
echo
echo "Ollama ${MODEL} is ready on ${BIND_ADDRESS}. Backup: ${BACKUP_DIR}"
-55
View File
@@ -1,55 +0,0 @@
# ==============================================================================
# Noveria.net Landingpage — Nginx Server Block
# ==============================================================================
# Diese Config gehört in den Host-Nginx unter /etc/nginx/sites-available/
# und muss via Symlink nach /etc/nginx/sites-enabled/ aktiviert werden.
#
# WICHTIG: Falls "noveria.net" oder "www.noveria.net" bereits in einem anderen
# Serverblock (z.B. dem nexus.noveria.net-Block) als server_name auftaucht,
# muss es dort entfernt werden, sonst schlägt nginx -t fehl.
# ==============================================================================
server {
listen 443 ssl http2;
server_name noveria.net www.noveria.net;
# SSL (gleiche Zertifikate wie nexus)
ssl_certificate /etc/letsencrypt/live/noveria.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/noveria.net/privkey.pem;
include /etc/nginx/snippets/ssl-params.conf;
# Security Header
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
location / {
proxy_pass http://127.0.0.1:18881;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# HTTP → HTTPS redirect
server {
listen 80;
server_name noveria.net www.noveria.net;
return 301 https://$host$request_uri;
}
# ==============================================================================
# Diagnose-Kommandos (auf dem Host auszuführen, nicht im Container!)
# ==============================================================================
# 1. Prüfen ob noveria.net bereits in bestehender Config referenziert wird
# grep -rn "noveria.net" /etc/nginx/sites-available/
# grep -rn "www.noveria.net" /etc/nginx/sites-available/
#
# 2. Config testen nach Änderung
# nginx -t
#
# 3. Nginx neuladen
# systemctl reload nginx
# ==============================================================================
-81
View File
@@ -1,81 +0,0 @@
# /etc/nginx/sites-available/nexus.noveria.net
# Symlink: ln -s /etc/nginx/sites-available/nexus.noveria.net /etc/nginx/sites-enabled/
server {
listen 80;
server_name nexus.noveria.net;
# Let's Encrypt challenge
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name nexus.noveria.net;
# SSL wird per certbot automatisch befüllt
ssl_certificate /etc/letsencrypt/live/nexus.noveria.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/nexus.noveria.net/privkey.pem;
# Security-Header
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
client_max_body_size 16m;
# Bridge-Endpunkte: Gateway-zu-Backend-Agent-Pfad
# X-Agent-Id wird durchgereicht für Agent-Identität
location /api/bridge/ {
proxy_pass http://127.0.0.1:18880;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Agent-Id $http_x_agent_id;
proxy_buffering off;
proxy_read_timeout 120s;
}
# Dashboard SSE stream: single dedicated non-buffered block.
location = /api/dashboard/live {
proxy_pass http://127.0.0.1:18880;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 1h;
proxy_send_timeout 1h;
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
add_header X-Accel-Buffering no always;
}
location / {
proxy_pass http://127.0.0.1:18880;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# API-Direktzugriff falls nötig
location /api/ {
proxy_pass http://127.0.0.1:18880;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
-107
View File
@@ -1,107 +0,0 @@
#!/bin/bash
# HTTPS-Setup für nexus.noveria.net
# Auf dem VPS-HOST ausführen!
set -e
echo "=== HTTPS Setup für nexus.noveria.net ==="
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# 1. Zuerst nur HTTP-Config ausrollen (keine SSL-Referenz!)
echo "[1/5] Installiere HTTP-only Nginx-Config..."
sudo tee /etc/nginx/sites-available/nexus.noveria.net > /dev/null << 'NGINXEOF'
server {
listen 80;
server_name nexus.noveria.net;
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
proxy_pass http://127.0.0.1:18880;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
NGINXEOF
sudo ln -sf /etc/nginx/sites-available/nexus.noveria.net /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
echo " ✅ HTTP-Config aktiv"
# 2. Firewall
echo "[2/5] Firewall..."
if command -v ufw &>/dev/null; then
sudo ufw allow 80/tcp 2>/dev/null || true
sudo ufw allow 443/tcp 2>/dev/null || true
echo " ✅ ufw: 80+443 offen"
else
echo " ⏭ ufw nicht installiert"
fi
# 3. HTTP-Test
echo "[3/5] Teste HTTP..."
sleep 1
STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://nexus.noveria.net)
echo " HTTP-Status: $STATUS"
# 4. Zertifikat holen
echo "[4/5] Fordere Let's-Encrypt-Zertifikat an..."
sudo certbot certonly --webroot -w /var/www/html -d nexus.noveria.net --non-interactive --agree-tos --email vmbao62@hotmail.de 2>&1 || {
echo " ⚠️ certbot fehlgeschlagen manuell nachholen:"
echo " sudo certbot --nginx -d nexus.noveria.net"
exit 1
}
echo " ✅ Zertifikat erhalten"
# 5. HTTPS-Config ausrollen
echo "[5/5] Aktiviere HTTPS-Config..."
sudo tee /etc/nginx/sites-available/nexus.noveria.net > /dev/null << 'NGINXSSL'
server {
listen 80;
server_name nexus.noveria.net;
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name nexus.noveria.net;
ssl_certificate /etc/letsencrypt/live/nexus.noveria.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/nexus.noveria.net/privkey.pem;
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
client_max_body_size 16m;
location / {
proxy_pass http://127.0.0.1:18880;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
NGINXSSL
sudo nginx -t && sudo systemctl reload nginx
echo " ✅ HTTPS aktiv"
# Test
echo ""
sleep 2
curl -s -o /dev/null -w "HTTPS-Status: %{http_code}\n" https://nexus.noveria.net
echo ""
echo "=== Fertig ==="
echo "Nexus: https://nexus.noveria.net"
+15 -23
View File
@@ -7,10 +7,9 @@
## CD-Philosophie (v3)
- **CI läuft automatisch** bei jedem Push → darf nie brechen
- **CD auto + manuell**: Automaticher Deploy nach CI-Success auf main (patch default), manueller Deploy mit voller Kontrolle via `workflow_dispatch`
- **Loop-Schutz**: Version-Bump-Commits enthalten `[skip ci]` — kein Re-Trigger der CI, kein Infinite-Loop
- **Main-Deploys** duerfen VERSION bumpen und einen Git-Tag setzen
- **Nicht-Main-Deploys** (anderer `git_ref`) deployen read-only und mutieren Git nicht
- **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
- 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)
@@ -20,7 +19,7 @@
**Trigger**:
- **Automatisch**: Nach erfolgreicher CI (`workflow_run` auf `CI - Build & Test`)
→ Default-Parameter: patch bump, all services, main ref
→ Deployt `main` mit dem im Repo gesetzten `VERSION`-Wert
- **Manuell**: Via Gitea Actions → `workflow_dispatch`
**Loop-Schutz**:
@@ -28,26 +27,19 @@
- 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 …
**Inputs** (nur bei `workflow_dispatch`):
| Input | Typ | Default | Beschreibung |
|---|---|---|---|
| `version_bump` | choice (patch/minor/major) | patch | Version-Bump-Typ |
| `service` | string | (all) | Einzelner Service oder alle |
| `no_cache` | boolean | false | Docker-Build-Cache deaktivieren |
| `git_ref` | string | main | Branch/Tag/Commit zum Deployen |
**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 des gewählten Git-Refs
3. Wenn `git_ref = main`: Version-Bump + Git-Tag + Push
4. Wenn `git_ref != main`: VERSION nur lesen, kein Push, kein Tag
5. **Safe Secret Handling**: `.env` wird aus Secret-Umgebungsvariablen in `/tmp/nexus-deploy-env` geschrieben (mode 600), **NICHT** im Workspace
6. Code-Sync zum Host-Deploy-Pfad
7. `docker compose build && up -d --wait --force-recreate`
8. `.env`-Tempfile wird mit `shred` gelöscht
9. Health-Check (exponentieller Backoff, 6 Versuche)
10. Smoke-Test (`/dashboard`, `/health`, `/api/v1/operations/snapshot` erwartet `401`)
11. Bei Fehler: Reviewer-Handoff-Meldung mit Job-URL
2. Checkout von `main`
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`
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
### Backup (`.gitea/workflows/backup.yaml`)
@@ -222,7 +214,7 @@ Stelle sicher, dass `.env` existiert und alle `***`-Platzhalter ersetzt sind.
2. `curl http://127.0.0.1:18880/health`
3. Falls `health=200`, aber `/dashboard` noch nicht `200` und `web` auf `Created` steht: `docker compose up -d web`
4. Danach extern `/dashboard`, `/health` und `/api/v1/operations/snapshot` erneut prüfen
- Der manuelle Helper [`ops/deploy.sh`](/home/node/.openclaw/workspace/nexus/ops/deploy.sh) verifiziert deshalb jetzt nicht mehr nur `/health`, sondern auch `/dashboard` und den Auth-Schutz der Operations-API.
- Der CD-Pfad verifiziert deshalb nicht mehr nur `/health`, sondern auch `/dashboard` und den Auth-Schutz der Operations-API.
## Offene Arbeit