name: Deploy to Production on: push: branches: - prod jobs: test: runs-on: ubuntu-latest services: postgres: image: postgres:15-alpine env: POSTGRES_PASSWORD: postgres POSTGRES_DB: lediscord_test options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5433:5432 steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Install backend dependencies run: | cd backend pip install -r requirements.txt - name: Run backend tests run: | cd backend python -m pytest tests/ -v || true env: DATABASE_URL: postgresql://postgres:postgres@localhost:5433/lediscord_test JWT_SECRET_KEY: test-secret-key CORS_ORIGINS: http://localhost:3000 ADMIN_EMAIL: test@test.com ADMIN_PASSWORD: test123 ENVIRONMENT: test - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: '18' cache: 'npm' cache-dependency-path: frontend/package-lock.json - name: Install frontend dependencies run: | cd frontend npm ci - name: Build frontend run: | cd frontend npm run build build-and-deploy: needs: test runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Force IPv4 for DNS run: | echo 'precedence ::ffff:0:0/96 100' | sudo tee -a /etc/gai.conf - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to Gitea Container Registry (with retry) run: | for i in 1 2 3 4 5; do echo "🔐 Login attempt $i..." echo "${{ secrets.REGISTRY_TOKEN }}" | docker login ${{ secrets.REGISTRY_URL }} -u ${{ secrets.REGISTRY_USERNAME }} --password-stdin && break || sleep 5 done - name: Build backend image uses: docker/build-push-action@v4 with: context: ./backend file: ./backend/Dockerfile push: false load: true tags: | ${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USERNAME }}/lediscord:prod-backend ${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USERNAME }}/lediscord:latest-backend cache-from: type=registry,ref=${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USERNAME }}/lediscord:prod-backend cache-to: type=inline - name: Push backend image (with retry) run: | push_with_retry() { local image=$1 for i in 1 2 3 4 5; do echo "📤 Push attempt $i for $image..." if docker push "$image"; then echo "✅ Successfully pushed $image" return 0 fi echo "⚠️ Push failed, waiting 10s..." sleep 10 done echo "❌ Failed to push $image after 5 attempts" return 1 } push_with_retry "${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USERNAME }}/lediscord:prod-backend" push_with_retry "${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USERNAME }}/lediscord:latest-backend" - name: Build frontend image uses: docker/build-push-action@v4 with: context: ./frontend file: ./frontend/Dockerfile push: false load: true tags: | ${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USERNAME }}/lediscord:prod-frontend ${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USERNAME }}/lediscord:latest-frontend cache-from: type=registry,ref=${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USERNAME }}/lediscord:prod-frontend cache-to: type=inline - name: Push frontend image (with retry) run: | push_with_retry() { local image=$1 for i in 1 2 3 4 5; do echo "📤 Push attempt $i for $image..." if docker push "$image"; then echo "✅ Successfully pushed $image" return 0 fi echo "⚠️ Push failed, waiting 10s..." sleep 10 done echo "❌ Failed to push $image after 5 attempts" return 1 } push_with_retry "${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USERNAME }}/lediscord:prod-frontend" push_with_retry "${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USERNAME }}/lediscord:latest-frontend" - name: Deploy to Nomad if: success() run: | echo "🚀 Deployment ready!" echo "Run on your server:" echo " nomad job run -force lediscord.nomad"