name: ๐Ÿš€ Deploy Nanaxe Server on: push: branches: [ main, master ] paths: - 'server/**' - '.github/workflows/**' pull_request: branches: [ main, master ] paths: - 'server/**' env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }}/nanaxe-api jobs: # ========================================== # Build and Test # ========================================== build: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - name: ๐Ÿ“ฅ Checkout repository uses: actions/checkout@v4 - name: ๐Ÿณ Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: ๐Ÿ“ฆ Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: ๐Ÿท๏ธ Extract metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch type=ref,event=pr type=sha,prefix={{branch}}- type=raw,value=latest,enable={{is_default_branch}} - name: ๐Ÿ”จ Build and push Docker image uses: docker/build-push-action@v5 with: context: ./server/api push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max platforms: linux/amd64,linux/arm64 - name: ๐Ÿงช Test Docker image run: | docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest node --version # ========================================== # Deploy to Production # ========================================== deploy: needs: build runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' environment: name: production url: http://${{ secrets.VPS_HOST }} steps: - name: ๐Ÿ“ฅ Checkout repository uses: actions/checkout@v4 - name: ๐Ÿ” Setup SSH uses: webfactory/ssh-agent@v0.8.0 with: ssh-private-key: ${{ secrets.VPS_SSH_KEY }} - name: ๐Ÿš€ Deploy to VPS env: VPS_HOST: ${{ secrets.VPS_HOST }} VPS_USER: ${{ secrets.VPS_USER }} DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }} run: | # Add host to known hosts mkdir -p ~/.ssh ssh-keyscan -H $VPS_HOST >> ~/.ssh/known_hosts # Create deployment script cat > deploy.sh << 'EOF' #!/bin/bash set -e echo "๐Ÿ”„ Starting deployment..." cd ${{ secrets.DEPLOY_PATH }} # Backup current state echo "๐Ÿ’พ Creating backup..." if [ -f docker-compose.yml ]; then docker compose exec -T postgres pg_dump -U gitea gitea > backup/db-$(date +%Y%m%d-%H%M%S).sql 2>/dev/null || true fi # Pull latest changes echo "๐Ÿ“ฅ Pulling latest code..." git fetch origin git reset --hard origin/$(git rev-parse --abbrev-ref HEAD) # Update environment variables echo "โš™๏ธ Updating environment..." cat > .env << 'ENVFILE' GITEA_DOMAIN=${{ secrets.GITEA_DOMAIN }} GITEA_TOKEN=${{ secrets.GITEA_TOKEN }} JWT_SECRET=${{ secrets.JWT_SECRET }} WEBDAV_USER=${{ secrets.WEBDAV_USER }} WEBDAV_PASSWORD=${{ secrets.WEBDAV_PASSWORD }} POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} API_PORT=8080 ENVFILE # Update image tag in docker-compose sed -i "s|image:.*nanaxe-api.*|image: ghcr.io/${{ github.repository }}/nanaxe-api:latest|" docker-compose.prod.yml || true # Pull and deploy echo "๐Ÿณ Pulling Docker images..." docker compose -f docker-compose.yml pull echo "๐Ÿš€ Starting services..." docker compose -f docker-compose.yml up -d --remove-orphans # Health check echo "๐Ÿฅ Running health checks..." sleep 10 # Check API health if curl -sf http://localhost:8080/health > /dev/null; then echo "โœ… API is healthy" else echo "โŒ API health check failed" docker compose logs --tail=50 api exit 1 fi # Check Gitea if curl -sf http://localhost:3000/api/v1/version > /dev/null; then echo "โœ… Gitea is healthy" else echo "โš ๏ธ Gitea may still be initializing" fi # Cleanup echo "๐Ÿงน Cleaning up..." docker system prune -f docker volume prune -f echo "โœ… Deployment completed successfully!" EOF chmod +x deploy.sh # Copy files to server echo "๐Ÿ“ค Copying files to server..." scp -r server/docker-compose.yml server/nginx $VPS_USER@$VPS_HOST:$DEPLOY_PATH/ scp deploy.sh $VPS_USER@$VPS_HOST:$DEPLOY_PATH/ # Execute deployment echo "๐Ÿš€ Executing deployment..." ssh $VPS_USER@$VPS_HOST "cd $DEPLOY_PATH && bash deploy.sh" - name: ๐Ÿ”” Notify on success if: success() run: | echo "โœ… Deployment completed successfully!" echo "๐ŸŒ Server: http://${{ secrets.VPS_HOST }}" - name: ๐Ÿ”” Notify on failure if: failure() run: | echo "โŒ Deployment failed!" echo "Check logs for details" # ========================================== # Rollback (manual trigger) # ========================================== rollback: runs-on: ubuntu-latest if: github.event_name == 'workflow_dispatch' environment: production steps: - name: ๐Ÿ” Setup SSH uses: webfactory/ssh-agent@v0.8.0 with: ssh-private-key: ${{ secrets.VPS_SSH_KEY }} - name: โฎ๏ธ Rollback to previous version env: VPS_HOST: ${{ secrets.VPS_HOST }} VPS_USER: ${{ secrets.VPS_USER }} DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }} run: | ssh-keyscan -H $VPS_HOST >> ~/.ssh/known_hosts ssh $VPS_USER@$VPS_HOST << 'EOF' cd $DEPLOY_PATH echo "โฎ๏ธ Rolling back..." # Get previous image PREVIOUS_IMAGE=$(docker images ghcr.io/${{ github.repository }}/nanaxe-api --format "{{.Repository}}:{{.Tag}}" | grep -v latest | head -1) if [ -n "$PREVIOUS_IMAGE" ]; then echo "Reverting to: $PREVIOUS_IMAGE" docker compose stop api docker compose rm -f api docker run -d --name nanaxe-api-rollback \ --network nanaxe-network \ -v mindmap-data:/app/mindmaps \ -e NODE_ENV=production \ -e PORT=8080 \ -p 8080:8080 \ $PREVIOUS_IMAGE echo "โœ… Rollback completed" else echo "โŒ No previous image found" exit 1 fi EOF