diff --git a/.gitea/workflows/ci-cd-fast.yml b/.gitea/workflows/ci-cd-fast.yml index 03c7e4f..578b7ce 100644 --- a/.gitea/workflows/ci-cd-fast.yml +++ b/.gitea/workflows/ci-cd-fast.yml @@ -222,12 +222,64 @@ jobs: - name: Wait for container to be ready run: | - sleep 10 - timeout 60 bash -c 'until curl -f http://localhost:3000/api/health; do sleep 2; done' + echo "โณ Waiting for container to be ready..." + sleep 15 + + # Check if container is actually running + if ! docker ps --filter "name=portfolio-app" --format "{{.Names}}" | grep -q "portfolio-app"; then + echo "โŒ Container failed to start" + echo "Container logs:" + docker logs portfolio-app --tail=50 + exit 1 + fi + + # Wait for health check with better error handling + echo "๐Ÿฅ Performing health check..." + for i in {1..40}; do + if curl -f http://localhost:3000/api/health > /dev/null 2>&1; then + echo "โœ… Application is healthy!" + break + fi + + # Check if container is still running + if ! docker ps --filter "name=portfolio-app" --format "{{.Names}}" | grep -q "portfolio-app"; then + echo "โŒ Container stopped during health check" + echo "Container logs:" + docker logs portfolio-app --tail=50 + exit 1 + fi + + echo "โณ Health check attempt $i/40..." + sleep 3 + done + + # Final health check + if ! curl -f http://localhost:3000/api/health > /dev/null 2>&1; then + echo "โŒ Health check timeout" + echo "Container logs:" + docker logs portfolio-app --tail=100 + exit 1 + fi - name: Health check run: | + echo "๐Ÿ” Final health verification..." + + # Check container status + docker ps --filter "name=portfolio-app" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" + + # Test health endpoint curl -f http://localhost:3000/api/health + echo "" + + # Test main page + if curl -f http://localhost:3000/ > /dev/null; then + echo "โœ… Main page is accessible" + else + echo "โŒ Main page is not accessible" + exit 1 + fi + echo "โœ… Deployment successful!" - name: Cleanup old images diff --git a/.gitea/workflows/ci-cd-simple.yml b/.gitea/workflows/ci-cd-simple.yml new file mode 100644 index 0000000..931548c --- /dev/null +++ b/.gitea/workflows/ci-cd-simple.yml @@ -0,0 +1,143 @@ +name: CI/CD Pipeline (Simple & Reliable) + +on: + push: + branches: [ production ] + +env: + NODE_VERSION: '20' + DOCKER_IMAGE: portfolio-app + CONTAINER_NAME: portfolio-app + +jobs: + production: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run linting + run: npm run lint + + - name: Run tests + run: npm run test + + - name: Build application + run: npm run build + + - name: Run security scan + run: | + echo "๐Ÿ” Running npm audit..." + npm audit --audit-level=high || echo "โš ๏ธ Some vulnerabilities found, but continuing..." + + - name: Verify secrets and variables + run: | + echo "๐Ÿ” Verifying secrets and variables..." + + # Check Variables + if [ -z "${{ vars.NEXT_PUBLIC_BASE_URL }}" ]; then + echo "โŒ NEXT_PUBLIC_BASE_URL variable is missing!" + exit 1 + fi + if [ -z "${{ vars.MY_EMAIL }}" ]; then + echo "โŒ MY_EMAIL variable is missing!" + exit 1 + fi + if [ -z "${{ vars.MY_INFO_EMAIL }}" ]; then + echo "โŒ MY_INFO_EMAIL variable is missing!" + exit 1 + fi + + # Check Secrets + if [ -z "${{ secrets.MY_PASSWORD }}" ]; then + echo "โŒ MY_PASSWORD secret is missing!" + exit 1 + fi + if [ -z "${{ secrets.MY_INFO_PASSWORD }}" ]; then + echo "โŒ MY_INFO_PASSWORD secret is missing!" + exit 1 + fi + if [ -z "${{ secrets.ADMIN_BASIC_AUTH }}" ]; then + echo "โŒ ADMIN_BASIC_AUTH secret is missing!" + exit 1 + fi + + echo "โœ… All required secrets and variables are present" + + - name: Deploy using improved script + run: | + echo "๐Ÿš€ Deploying using improved deployment script..." + + # Set environment variables for the deployment script + export MY_PASSWORD="${{ secrets.MY_PASSWORD }}" + export MY_INFO_PASSWORD="${{ secrets.MY_INFO_PASSWORD }}" + export ADMIN_BASIC_AUTH="${{ secrets.ADMIN_BASIC_AUTH }}" + + # Make the script executable + chmod +x ./scripts/gitea-deploy.sh + + # Run the deployment script + ./scripts/gitea-deploy.sh + env: + NODE_ENV: ${{ vars.NODE_ENV }} + LOG_LEVEL: ${{ vars.LOG_LEVEL }} + NEXT_PUBLIC_BASE_URL: ${{ vars.NEXT_PUBLIC_BASE_URL }} + NEXT_PUBLIC_UMAMI_URL: ${{ vars.NEXT_PUBLIC_UMAMI_URL }} + NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${{ vars.NEXT_PUBLIC_UMAMI_WEBSITE_ID }} + MY_EMAIL: ${{ vars.MY_EMAIL }} + MY_INFO_EMAIL: ${{ vars.MY_INFO_EMAIL }} + MY_PASSWORD: ${{ secrets.MY_PASSWORD }} + MY_INFO_PASSWORD: ${{ secrets.MY_INFO_PASSWORD }} + ADMIN_BASIC_AUTH: ${{ secrets.ADMIN_BASIC_AUTH }} + + - name: Final verification + run: | + echo "๐Ÿ” Final verification..." + + # Wait a bit more to ensure everything is stable + sleep 10 + + # Check if container is running + if docker ps --filter "name=${{ env.CONTAINER_NAME }}" --format "{{.Names}}" | grep -q "${{ env.CONTAINER_NAME }}"; then + echo "โœ… Container is running" + else + echo "โŒ Container is not running" + docker ps -a + exit 1 + fi + + # Check health endpoint + if curl -f http://localhost:3000/api/health; then + echo "โœ… Health check passed" + else + echo "โŒ Health check failed" + echo "Container logs:" + docker logs ${{ env.CONTAINER_NAME }} --tail=50 + exit 1 + fi + + # Check main page + if curl -f http://localhost:3000/ > /dev/null; then + echo "โœ… Main page is accessible" + else + echo "โŒ Main page is not accessible" + exit 1 + fi + + echo "๐ŸŽ‰ Deployment successful!" + + - name: Cleanup old images + run: | + echo "๐Ÿงน Cleaning up old images..." + docker image prune -f + docker system prune -f + echo "โœ… Cleanup completed" diff --git a/.gitea/workflows/ci-cd-zero-downtime-fixed.yml b/.gitea/workflows/ci-cd-zero-downtime-fixed.yml index d540a4f..835c6d8 100644 --- a/.gitea/workflows/ci-cd-zero-downtime-fixed.yml +++ b/.gitea/workflows/ci-cd-zero-downtime-fixed.yml @@ -114,38 +114,87 @@ jobs: MY_INFO_PASSWORD: ${{ secrets.MY_INFO_PASSWORD }} ADMIN_BASIC_AUTH: ${{ secrets.ADMIN_BASIC_AUTH }} - - name: Wait for container to be ready + - name: Wait for containers to be ready run: | echo "โณ Waiting for containers to be ready..." - sleep 15 + sleep 20 + + # Check if all containers are running + echo "๐Ÿ“Š Checking container status..." + docker compose -f docker-compose.zero-downtime.yml ps + + # Wait for application containers to be healthy + echo "๐Ÿฅ Waiting for application containers to be healthy..." + for i in {1..30}; do + # Check if both app containers are healthy + if docker exec portfolio-app-1 curl -f http://localhost:3000/api/health > /dev/null 2>&1 && \ + docker exec portfolio-app-2 curl -f http://localhost:3000/api/health > /dev/null 2>&1; then + echo "โœ… Both application containers are healthy!" + break + fi + echo "โณ Waiting for application containers... ($i/30)" + sleep 3 + done # Wait for nginx to be healthy - for i in {1..30}; do + echo "๐ŸŒ Waiting for nginx to be healthy..." + for i in {1..20}; do if curl -f http://localhost/health > /dev/null 2>&1; then echo "โœ… Nginx is healthy!" break fi - echo "โณ Waiting for nginx... ($i/30)" + echo "โณ Waiting for nginx... ($i/20)" sleep 2 done - name: Health check run: | - echo "๐Ÿ” Running health checks..." + echo "๐Ÿ” Running comprehensive health checks..." + + # Check container status + echo "๐Ÿ“Š Container status:" + docker compose -f docker-compose.zero-downtime.yml ps + + # Check individual application containers + echo "๐Ÿฅ Checking individual application containers..." + if docker exec portfolio-app-1 curl -f http://localhost:3000/api/health; then + echo "โœ… portfolio-app-1 health check passed!" + else + echo "โŒ portfolio-app-1 health check failed!" + docker logs portfolio-app-1 --tail=20 + exit 1 + fi + + if docker exec portfolio-app-2 curl -f http://localhost:3000/api/health; then + echo "โœ… portfolio-app-2 health check passed!" + else + echo "โŒ portfolio-app-2 health check failed!" + docker logs portfolio-app-2 --tail=20 + exit 1 + fi # Check nginx health if curl -f http://localhost/health; then echo "โœ… Nginx health check passed!" else echo "โŒ Nginx health check failed!" + docker logs portfolio-nginx --tail=20 exit 1 fi # Check application health through nginx if curl -f http://localhost/api/health; then - echo "โœ… Application health check passed!" + echo "โœ… Application health check through nginx passed!" else - echo "โŒ Application health check failed!" + echo "โŒ Application health check through nginx failed!" + exit 1 + fi + + # Check main page through nginx + if curl -f http://localhost/ > /dev/null; then + echo "โœ… Main page is accessible through nginx!" + else + echo "โŒ Main page is not accessible through nginx!" exit 1 fi diff --git a/DEPLOYMENT-FIXES.md b/DEPLOYMENT-FIXES.md new file mode 100644 index 0000000..50d42e2 --- /dev/null +++ b/DEPLOYMENT-FIXES.md @@ -0,0 +1,119 @@ +# Deployment Fixes for Gitea Actions + +## Problem Summary +The Gitea Actions were failing with "Connection refused" errors when trying to connect to localhost:3000. This was caused by several issues: + +1. **Incorrect Dockerfile path**: The Dockerfile was trying to copy from the wrong standalone build path +2. **Missing environment variables**: The deployment scripts weren't providing necessary environment variables +3. **Insufficient health check timeouts**: The health checks were too aggressive +4. **Poor error handling**: The workflows didn't provide enough debugging information + +## Fixes Applied + +### 1. Fixed Dockerfile +- **Issue**: Dockerfile was trying to copy from `/app/.next/standalone/portfolio` but the actual path was `/app/.next/standalone/app` +- **Fix**: Updated the Dockerfile to use the correct path: `/app/.next/standalone/app` +- **File**: `Dockerfile` + +### 2. Enhanced Deployment Scripts +- **Issue**: Missing environment variables and poor error handling +- **Fix**: Updated `scripts/gitea-deploy.sh` with: + - Proper environment variable handling + - Extended health check timeout (120 seconds) + - Better container status monitoring + - Improved error messages and logging +- **File**: `scripts/gitea-deploy.sh` + +### 3. Created Simplified Deployment Script +- **Issue**: Complex deployment with database dependencies +- **Fix**: Created `scripts/gitea-deploy-simple.sh` for testing without database dependencies +- **File**: `scripts/gitea-deploy-simple.sh` + +### 4. Fixed Next.js Configuration +- **Issue**: Duplicate `serverRuntimeConfig` properties causing build failures +- **Fix**: Removed duplicate configuration and fixed the standalone build path +- **File**: `next.config.ts` + +### 5. Improved Gitea Actions Workflows +- **Issue**: Poor health check logic and insufficient error handling +- **Fix**: Updated all workflow files with: + - Better container status checking + - Extended health check timeouts + - Comprehensive error logging + - Container log inspection on failures +- **Files**: + - `.gitea/workflows/ci-cd-fast.yml` + - `.gitea/workflows/ci-cd-zero-downtime-fixed.yml` + - `.gitea/workflows/ci-cd-simple.yml` (new) + +## Available Workflows + +### 1. CI/CD Simple (Recommended) +- **File**: `.gitea/workflows/ci-cd-simple.yml` +- **Description**: Uses the improved deployment script with comprehensive error handling +- **Best for**: Reliable deployments with good debugging + +### 2. CI/CD Fast +- **File**: `.gitea/workflows/ci-cd-fast.yml` +- **Description**: Fast deployment with rolling updates +- **Best for**: Production deployments with zero downtime + +### 3. CI/CD Zero Downtime +- **File**: `.gitea/workflows/ci-cd-zero-downtime-fixed.yml` +- **Description**: Full zero-downtime deployment with nginx load balancer +- **Best for**: Production deployments requiring high availability + +## Testing the Fixes + +### Local Testing +```bash +# Test the simplified deployment script +./scripts/gitea-deploy-simple.sh + +# Test the full deployment script +./scripts/gitea-deploy.sh +``` + +### Verification +```bash +# Check if the application is running +curl -f http://localhost:3000/api/health + +# Check the main page +curl -f http://localhost:3000/ +``` + +## Environment Variables Required + +### Variables (in Gitea repository settings) +- `NODE_ENV`: production +- `LOG_LEVEL`: info +- `NEXT_PUBLIC_BASE_URL`: https://dk0.dev +- `NEXT_PUBLIC_UMAMI_URL`: https://analytics.dk0.dev +- `NEXT_PUBLIC_UMAMI_WEBSITE_ID`: b3665829-927a-4ada-b9bb-fcf24171061e +- `MY_EMAIL`: contact@dk0.dev +- `MY_INFO_EMAIL`: info@dk0.dev + +### Secrets (in Gitea repository settings) +- `MY_PASSWORD`: Your email password +- `MY_INFO_PASSWORD`: Your info email password +- `ADMIN_BASIC_AUTH`: admin:your_secure_password_here + +## Troubleshooting + +### If deployment still fails: +1. Check the Gitea Actions logs for specific error messages +2. Verify all environment variables and secrets are set correctly +3. Check if the Docker image builds successfully locally +4. Ensure the health check endpoint is accessible + +### Common Issues: +- **"Connection refused"**: Container failed to start or crashed +- **"Health check timeout"**: Application is taking too long to start +- **"Build failed"**: Docker build issues, check Dockerfile and dependencies + +## Next Steps +1. Push these changes to your Gitea repository +2. The Actions should now work without the "Connection refused" errors +3. Monitor the deployment logs for any remaining issues +4. Consider using the "CI/CD Simple" workflow for the most reliable deployments diff --git a/logs/gitea-deploy-simple.log b/logs/gitea-deploy-simple.log index b4b695a..cb907f1 100644 --- a/logs/gitea-deploy-simple.log +++ b/logs/gitea-deploy-simple.log @@ -41,3 +41,25 @@ [2025-09-13 23:36:54] ๐Ÿ“Š Container name: portfolio-app-simple [2025-09-13 23:36:54] ๐Ÿ“ Logs: docker logs portfolio-app-simple Sat Sep 13 23:36:54 CEST 2025: Simplified Gitea deployment successful - Port: 3000 - Image: portfolio-app:20250913-233632 +[2025-09-13 23:46:31] ๐Ÿš€ Starting simplified Gitea deployment for portfolio +[2025-09-13 23:46:31] ๐Ÿ”จ Step 1: Building application... +[2025-09-13 23:46:31] ๐Ÿ“ฆ Building Next.js application... +[SUCCESS] โœ… Application built successfully +[2025-09-13 23:46:54] ๐Ÿณ Step 2: Docker operations... +[2025-09-13 23:46:54] ๐Ÿ—๏ธ Building Docker image... +[SUCCESS] โœ… Docker image built successfully +[2025-09-13 23:48:01] ๐Ÿš€ Step 3: Deploying application... +[2025-09-13 23:48:01] ๐Ÿš€ Starting new container on port 3000... +[2025-09-13 23:48:01] โณ Waiting for container to be ready... +[2025-09-13 23:48:21] ๐Ÿฅ Performing health check... +[SUCCESS] โœ… Application is healthy! +[2025-09-13 23:48:21] โœ… Step 4: Verifying deployment... +[SUCCESS] โœ… Main page is accessible +[2025-09-13 23:48:22] ๐Ÿ“Š Container status: +[2025-09-13 23:48:22] ๐Ÿ“ˆ Resource usage: +[SUCCESS] ๐ŸŽ‰ Simplified Gitea deployment completed successfully! +[2025-09-13 23:48:23] ๐ŸŒ Application is available at: http://localhost:3000 +[2025-09-13 23:48:23] ๐Ÿฅ Health check endpoint: http://localhost:3000/api/health +[2025-09-13 23:48:23] ๐Ÿ“Š Container name: portfolio-app-simple +[2025-09-13 23:48:23] ๐Ÿ“ Logs: docker logs portfolio-app-simple +Sat Sep 13 23:48:23 CEST 2025: Simplified Gitea deployment successful - Port: 3000 - Image: portfolio-app:20250913-234801