diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml new file mode 100644 index 0000000..61efbb4 --- /dev/null +++ b/.forgejo/workflows/deploy.yml @@ -0,0 +1,100 @@ +name: Deploy to Production + +on: + push: + branches: [ main ] + +jobs: + deploy: + name: Deploy to Server + runs-on: ubuntu-latest + + steps: + - name: Deploy via SSH + uses: appleboy/ssh-action@v1.1.0 + with: + host: ${{ secrets.SERVER_HOST }} + username: ${{ secrets.SERVER_USER }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + script: | + set -e + + echo "๐Ÿš€ Starting deployment..." + + # Navigate to project directory + cd /root/docfast + + # Check current git status + echo "๐Ÿ“‹ Current status:" + git status --short || true + + # Pull latest changes + echo "๐Ÿ“ฅ Pulling latest changes..." + git fetch origin + git pull origin main + + # Tag current running image for rollback + echo "๐Ÿท๏ธ Tagging current image for rollback..." + TIMESTAMP=$(date +%Y%m%d-%H%M%S) + docker tag docfast-docfast:latest docfast-docfast:rollback-$TIMESTAMP || echo "No existing image to tag" + + # Build new image + echo "๐Ÿ”จ Building new Docker image..." + docker compose build --no-cache + + # Stop services gracefully + echo "โน๏ธ Stopping services..." + docker compose down --timeout 30 + + # Start services + echo "โ–ถ๏ธ Starting services..." + docker compose up -d + + # Wait for service to be ready + echo "โฑ๏ธ Waiting for service to be ready..." + for i in {1..30}; do + if curl -f -s http://127.0.0.1:3100/health > /dev/null; then + echo "โœ… Service is healthy!" + break + fi + if [ $i -eq 30 ]; then + echo "โŒ Service failed to start - initiating rollback..." + docker compose down + + # Try to rollback to previous image + ROLLBACK_IMAGE=$(docker images --format "table {{.Repository}}:{{.Tag}}" | grep "docfast-docfast:rollback-" | head -n1 | tr -s ' ' | cut -d' ' -f1) + if [ ! -z "$ROLLBACK_IMAGE" ]; then + echo "๐Ÿ”„ Rolling back to $ROLLBACK_IMAGE" + docker tag $ROLLBACK_IMAGE docfast-docfast:latest + docker compose up -d + + # Wait for rollback to be healthy + sleep 10 + if curl -f -s http://127.0.0.1:3100/health > /dev/null; then + echo "โœ… Rollback successful" + else + echo "โŒ Rollback failed - manual intervention required" + fi + else + echo "โŒ No rollback image available" + fi + + exit 1 + fi + echo "โณ Attempt $i/30 - waiting 5 seconds..." + sleep 5 + done + + # Cleanup old rollback images (keep last 5) + echo "๐Ÿงน Cleaning up old rollback images..." + docker images --format "table {{.Repository}}:{{.Tag}}" | grep "docfast-docfast:rollback-" | tail -n +6 | awk '{print $1":"$2}' | xargs -r docker rmi || true + + # Final health check and status + echo "๐Ÿ” Final health check..." + HEALTH_STATUS=$(curl -f -s http://127.0.0.1:3100/health || echo "UNHEALTHY") + echo "Health status: $HEALTH_STATUS" + + echo "๐Ÿ“Š Service status:" + docker compose ps + + echo "๐ŸŽ‰ Deployment completed successfully!" \ No newline at end of file diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..f9ca614 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,77 @@ +# DocFast CI/CD Deployment + +This repository uses Forgejo Actions for automated deployment to production. + +## Setup Instructions + +### 1. Repository Secrets + +Go to repository settings โ†’ Actions โ†’ Secrets and add these secrets: + +- **SERVER_HOST**: `167.235.156.214` +- **SERVER_USER**: `root` +- **SSH_PRIVATE_KEY**: The private SSH key content from `/home/openclaw/.ssh/docfast` + +### 2. How Deployment Works + +**Trigger**: Push to `main` branch +**Process**: +1. SSH to production server +2. Pull latest code from git +3. Tag current Docker image for rollback +4. Build new Docker image +5. Stop current services +6. Start new services +7. Health check at `http://127.0.0.1:3100/health` +8. Rollback automatically if health check fails + +### 3. Rollback Procedure + +**Automatic Rollback**: +- Happens automatically if deployment fails health checks +- Reverts to the previously tagged image + +**Manual Rollback**: +```bash +# On the production server +cd /root/docfast +./scripts/rollback.sh +``` + +**Emergency Rollback via SSH**: +```bash +ssh root@167.235.156.214 +cd /root/docfast +docker compose down +docker tag docfast-docfast:rollback-YYYYMMDD-HHMMSS docfast-docfast:latest +docker compose up -d +``` + +### 4. Monitoring + +- **Health Check**: `curl http://127.0.0.1:3100/health` +- **Service Status**: `docker compose ps` +- **Logs**: `docker compose logs -f` + +### 5. File Structure + +``` +.forgejo/workflows/deploy.yml # Main deployment workflow +scripts/rollback.sh # Manual rollback script +scripts/setup-secrets.sh # Helper for setting up secrets +DEPLOYMENT.md # This documentation +``` + +### 6. Testing the Pipeline + +1. Make a small change (e.g., bump version comment) +2. Commit and push to main branch +3. Check Actions tab in Forgejo to see deployment progress +4. Verify service is running with `curl http://127.0.0.1:3100/health` + +## Troubleshooting + +- **SSH Issues**: Ensure SSH key is properly added to secrets +- **Docker Build Issues**: Check server has enough disk space and memory +- **Health Check Fails**: Check if service is binding to correct port (3100) +- **Permission Issues**: Ensure user has Docker privileges on server \ No newline at end of file diff --git a/scripts/rollback.sh b/scripts/rollback.sh new file mode 100755 index 0000000..4004a31 --- /dev/null +++ b/scripts/rollback.sh @@ -0,0 +1,72 @@ +#!/bin/bash +set -e + +echo "๐Ÿ”„ DocFast Rollback Script" +echo "==========================" + +# Check if we're on the server +if [ ! -d "/root/docfast" ]; then + echo "โŒ This script should be run on the production server" + exit 1 +fi + +cd /root/docfast + +# List available rollback images +echo "๐Ÿ“‹ Available rollback images:" +ROLLBACK_IMAGES=$(docker images --format "table {{.Repository}}:{{.Tag}}\t{{.CreatedAt}}" | grep "docfast-docfast:rollback-" | head -10) + +if [ -z "$ROLLBACK_IMAGES" ]; then + echo "โŒ No rollback images available" + exit 1 +fi + +echo "$ROLLBACK_IMAGES" +echo "" + +# Get the most recent rollback image +LATEST_ROLLBACK=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "docfast-docfast:rollback-" | head -n1) + +if [ -z "$LATEST_ROLLBACK" ]; then + echo "โŒ No rollback image found" + exit 1 +fi + +echo "๐ŸŽฏ Will rollback to: $LATEST_ROLLBACK" +echo "" + +# Confirm rollback +read -p "โš ๏ธ Are you sure you want to rollback? (y/N): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "โŒ Rollback cancelled" + exit 1 +fi + +echo "๐Ÿ›‘ Stopping current services..." +docker compose down --timeout 30 + +echo "๐Ÿ”„ Rolling back to $LATEST_ROLLBACK..." +docker tag $LATEST_ROLLBACK docfast-docfast:latest + +echo "โ–ถ๏ธ Starting services..." +docker compose up -d + +echo "โฑ๏ธ Waiting for service to be ready..." +for i in {1..20}; do + if curl -f -s http://127.0.0.1:3100/health > /dev/null; then + echo "โœ… Rollback successful! Service is healthy." + break + fi + if [ $i -eq 20 ]; then + echo "โŒ Rollback failed - service is not responding" + exit 1 + fi + echo "โณ Attempt $i/20 - waiting 3 seconds..." + sleep 3 +done + +echo "๐Ÿ“Š Service status:" +docker compose ps + +echo "๐ŸŽ‰ Rollback completed successfully!" \ No newline at end of file diff --git a/scripts/setup-secrets.sh b/scripts/setup-secrets.sh new file mode 100755 index 0000000..90c7cae --- /dev/null +++ b/scripts/setup-secrets.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +echo "๐Ÿ” Forgejo Repository Secrets Setup" +echo "====================================" + +# Source credentials to get Forgejo token +source /home/openclaw/.openclaw/workspace/.credentials/docfast.env + +# Repository secrets to set up +REPO_URL="https://git.cloonar.com/api/v1/repos/openclawd/docfast/actions/secrets" + +echo "Setting up repository secrets for CI/CD..." + +# Server host +echo "๐Ÿ“ก Setting SERVER_HOST..." +curl -X PUT "$REPO_URL/SERVER_HOST" \ + -H "Authorization: token $FORGEJO_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"data":"167.235.156.214"}' \ + --silent + +# Server user +echo "๐Ÿ‘ค Setting SERVER_USER..." +curl -X PUT "$REPO_URL/SERVER_USER" \ + -H "Authorization: token $FORGEJO_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"data":"root"}' \ + --silent + +# SSH Private Key +echo "๐Ÿ”‘ Setting SSH_PRIVATE_KEY..." +SSH_KEY_CONTENT=$(cat /home/openclaw/.ssh/docfast | jq -Rs .) +curl -X PUT "$REPO_URL/SSH_PRIVATE_KEY" \ + -H "Authorization: token $FORGEJO_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"data\":$SSH_KEY_CONTENT}" \ + --silent + +echo "โœ… Repository secrets have been configured!" +echo "" +echo "๐Ÿ” To verify, check: https://git.cloonar.com/openclawd/docfast/settings/actions/secrets" \ No newline at end of file