name: Deploy to Production run-name: 🚀 Deploy ${{ inputs.bump_version || 'patch' }} by @${{ gitea.actor }} # ─────────────────────────────────────────────────── # Trigger: automatic after CI success, or manual dispatch. # Runner: uses ubuntu-latest label (consistently present on # runner id=5: linux,dotnet,node,deploy,ubuntu-latest,…). # Standard labels avoid custom-label matching edge cases. # ─────────────────────────────────────────────────── on: workflow_run: workflows: ["CI - Build & Test"] types: [completed] branches: [main] workflow_dispatch: inputs: bump_version: description: 'Version bump (Major=x.0.0, Minor=1.x.0 features, Patch=1.0.x fixes)' required: false default: 'patch' type: string options: - 'patch' - 'minor' - 'major' service: description: 'Service to deploy (empty = all)' required: false default: '' type: string no_cache: description: 'Disable build cache' required: false default: false type: boolean jobs: deploy: name: Deploy Nexus runs-on: ubuntu-latest if: ${{ gitea.event_name != 'workflow_run' || gitea.event.workflow_run.conclusion == 'success' }} steps: # ── Step 1: Checkout ───────────────────── - name: Checkout latest code uses: actions/checkout@v4 with: fetch-depth: 0 fetch-tags: true # ── Step 2: Version bump (race-free) ───── # Derives current version from git tags (not VERSION file) to # avoid race conditions where tag exists but VERSION is stale. # Uses --force on tag+push to handle retries after failed runs. - name: Version Bump run: | set -euo pipefail # Source of truth: latest git tag TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") CURRENT_VERSION="${TAG#v}" echo "📦 Current version (from git tags): $CURRENT_VERSION" MAJOR=$(echo "$CURRENT_VERSION" | cut -d. -f1) MINOR=$(echo "$CURRENT_VERSION" | cut -d. -f2) PATCH=$(echo "$CURRENT_VERSION" | cut -d. -f3) case "${{ inputs.bump_version }}" in major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;; minor) MINOR=$((MINOR + 1)); PATCH=0 ;; patch|*) PATCH=$((PATCH + 1)) ;; esac NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}" echo "🏷️ New version: $NEW_VERSION" echo "$NEW_VERSION" > VERSION git config user.email "devops@noveria.net" git config user.name "DevOps" git add VERSION git commit -m "chore: bump version to v${NEW_VERSION} [skip ci]" # --force avoids "tag already exists" when re-running after a failed attempt git tag -f "v${NEW_VERSION}" git push "https://devops:${{ secrets.GIT_TOKEN }}@git.noveria.net/bao/nexus.git" HEAD:main --force --tags echo "✅ Version bumped to v${NEW_VERSION}" # ── Step 3: Provision .env on host ──────── # The sync step excludes .env for security, so we re-create it # from Gitea secrets at the host deploy path BEFORE syncing code. - name: Create .env on host run: | cat > /opt/openclaw/data/openclaw/workspace/nexus/.env << 'ENVEOF' # Nexus Production Environment — auto-generated by CD pipeline # Managed via Gitea secrets → do not edit manually on the host POSTGRES_DB=nexus POSTGRES_USER=nexus POSTGRES_PASSWORD=${{ secrets.ENV_POSTGRES_PASSWORD }} JWT_KEY=${{ secrets.ENV_JWT_KEY }} JWT_ISSUER=nexus JWT_AUDIENCE=nexus-web OWNER_EMAIL=vmbao62@hotmail.de OWNER_PASSWORD=${{ secrets.ENV_OWNER_PASSWORD }} OWNER_DISPLAY_NAME= OPENCLAW_BASE_URL=http://host.docker.internal:18789 OPENCLAW_GATEWAY_TOKEN=${{ secrets.ENV_OPENCLAW_TOKEN }} OPENCLAW_GATEWAY_PASSWORD= ENVEOF echo "✅ .env created at host deploy path" # ── Step 4: Sync code to host ───────────── - name: Sync code to host deploy path run: | docker run --rm \ -v "${{ gitea.workspace }}:/src:ro" \ -v /opt/openclaw/data/openclaw/workspace/nexus:/dest \ alpine:latest \ sh -c " cd /src && \ find . -mindepth 1 -maxdepth 1 \ ! -name .git \ ! -name .env \ -exec cp -a {} /dest/ \; " # ── Step 5: Docker Buildx ───────────────── - name: Set up Docker Buildx run: docker buildx create --use 2>/dev/null || true # ── Step 6: Build & Deploy ──────────────── - name: Build & Deploy run: | BUILD_ARGS="" if [ "${{ inputs.no_cache }}" = "true" ]; then BUILD_ARGS="--no-cache" fi docker run --rm \ -v /opt/openclaw/data/openclaw/workspace/nexus:/workspace/nexus \ -v /var/run/docker.sock:/var/run/docker.sock \ -w /workspace/nexus \ docker:cli \ sh -c " set -e if [ -n '\${{ inputs.service }}' ]; then echo '🚀 Deploying service: \${{ inputs.service }}' docker compose build \$BUILD_ARGS \${{ inputs.service }} docker compose up -d --force-recreate \${{ inputs.service }} else echo '🚀 Deploying all services' docker compose build \$BUILD_ARGS docker compose up -d --force-recreate fi " # ── Step 7: Health Check ────────────────── - name: Health Check run: | sleep 5 echo "🏥 Health check..." for i in 1 2 3 4 5 6; do if curl -sf --max-time 10 https://nexus.noveria.net/health; then echo "" echo "✅ Health check passed" break fi echo "⏳ Retry $i/6..." sleep 5 done # ── Step 8: Smoke test ──────────────────── - name: Verify (smoke test) run: | echo "🔍 Smoke test..." HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" https://nexus.noveria.net/dashboard) echo "Dashboard: HTTP $HTTP_CODE" if [ "$HTTP_CODE" != "200" ]; then echo "❌ Dashboard not reachable!" exit 1 fi echo "✅ Deployment verified"