feat: add deploy-now workflow as dispatch-only fallback
CI - Build & Test / Backend (.NET) (push) Has been cancelled
CI - Build & Test / Frontend (Vue/TS) (push) Has been cancelled
CI - Build & Test / Security Check (push) Has been cancelled

This commit is contained in:
2026-06-21 21:35:15 +02:00
parent 784cb4eeee
commit 7a2d5ded20
+209
View File
@@ -0,0 +1,209 @@
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 (secrets + host .env → temp file)
run: |
set -euo pipefail
HOST_OWNER_PASSWORD=""
if [ -f "${DEPLOY_PATH}/.env" ]; then
HOST_OWNER_PASSWORD=$(grep '^OWNER_PASSWORD=' "${DEPLOY_PATH}/.env" | cut -d= -f2- || true)
fi
if [ -z "${HOST_OWNER_PASSWORD}" ]; then
echo "ERROR: OWNER_PASSWORD not found in ${DEPLOY_PATH}/.env"
exit 1
fi
cat > "${ENV_TMPFILE}" <<ENVEOF
POSTGRES_DB=nexus
POSTGRES_USER=nexus
POSTGRES_PASSWORD=${ENV_POSTGRES_PASSWORD}
JWT_KEY=${ENV_JWT_KEY}
JWT_ISSUER=nexus
JWT_AUDIENCE=nexus-web
OWNER_EMAIL=vmbao62@hotmail.de
OWNER_PASSWORD=${HOST_OWNER_PASSWORD}
OWNER_DISPLAY_NAME=
OPENCLAW_BASE_URL=http://host.docker.internal:18789
OPENCLAW_GATEWAY_TOKEN=${ENV_OPENCLAW_TOKEN}
OPENCLAW_GATEWAY_PASSWORD=
ENVEOF
chmod 600 "${ENV_TMPFILE}"
echo "OK .env written"
- 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"
- name: Build and Deploy
run: |
set -euo pipefail
cat > /tmp/nexus-deploy-script.sh << 'DEPLOYSCRIPT'
#!/bin/sh
set -e
trap 'rm -f /tmp/nexus-deploy-env' EXIT
cat > /tmp/nexus-deploy-env
docker compose --env-file /tmp/nexus-deploy-env down --remove-orphans 2>/dev/null || true
docker rm -f nexus-postgres-1 nexus-api-1 nexus-web-1 2>/dev/null || true
PG_VOL=$(docker volume ls -q --filter name=nexus-postgres 2>/dev/null | head -1)
if [ -n "$PG_VOL" ]; then
echo "Checking postgres WAL integrity..."
docker run --rm -v "$PG_VOL:/var/lib/postgresql/data" --entrypoint sh postgres:17-alpine -c "pg_resetwal -f /var/lib/postgresql/data && echo WAL reset OK" 2>&1 || echo "pg_resetwal failed (may be benign)"
else
echo "Postgres volume not found - will be created fresh"
fi
echo "Deploying all services"
docker compose --env-file /tmp/nexus-deploy-env build
docker compose --env-file /tmp/nexus-deploy-env up -d --force-recreate
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 \
-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 "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 ""
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 "========================================"