Some checks failed
Build and Deploy Next.js Blog to Production / 🔍 Code Quality Checks (push) Failing after 5m36s
Build and Deploy Next.js Blog to Production / 🏗️ Build and Push Docker Image (push) Has been skipped
Build and Deploy Next.js Blog to Production / 🚀 Deploy to Production (push) Has been skipped
342 lines
13 KiB
YAML
342 lines
13 KiB
YAML
# Gitea Actions Workflow for Next.js Blog Application
|
|
# This workflow builds a Docker image and deploys it to production
|
|
#
|
|
# Workflow triggers:
|
|
# - Push to master branch (automatic deployment)
|
|
# - Manual trigger via workflow_dispatch
|
|
#
|
|
# Required Secrets (configure in Gitea repository settings):
|
|
# - PRODUCTION_HOST: IP address or hostname of production server
|
|
# - PRODUCTION_USER: SSH username (e.g., 'deployer')
|
|
# - SSH_PRIVATE_KEY: Private SSH key for authentication
|
|
# - REGISTRY_USERNAME: Docker registry username (optional, if registry requires auth)
|
|
# - REGISTRY_PASSWORD: Docker registry password (optional, if registry requires auth)
|
|
#
|
|
# Environment Variables (configured below):
|
|
# - REGISTRY: Docker registry URL
|
|
# - IMAGE_NAME: Docker image name
|
|
|
|
name: Build and Deploy Next.js Blog to Production
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- master # Trigger on push to master branch
|
|
workflow_dispatch: # Allow manual trigger from Gitea UI
|
|
|
|
env:
|
|
# Docker registry configuration
|
|
# Update this to match your private registry URL
|
|
REGISTRY: repository.workspace:5000
|
|
IMAGE_NAME: mypage
|
|
|
|
jobs:
|
|
# ============================================
|
|
# Job 1: Code Quality Checks (Linting)
|
|
# ============================================
|
|
lint:
|
|
name: 🔍 Code Quality Checks
|
|
runs-on: ubuntu-latest
|
|
# env:
|
|
# ACTIONS_RUNTIME_URL: http://192.168.1.53:3000 # Setează la nivel de job
|
|
|
|
steps:
|
|
- name: 🔎 Checkout code
|
|
uses: actions/checkout@v4
|
|
# with:
|
|
# github-server-url: ${{ env.ACTIONS_RUNTIME_URL }}
|
|
|
|
- name: 📦 Setup Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "20"
|
|
cache: "npm"
|
|
|
|
- name: 📥 Install dependencies
|
|
run: npm ci
|
|
|
|
- name: 🔍 Run ESLint
|
|
run: npm run lint
|
|
|
|
- name: 💅 Check code formatting (Prettier)
|
|
run: npm run format:check
|
|
|
|
- name: 🔤 TypeScript type checking
|
|
run: npx tsc --noEmit
|
|
|
|
- name: ✅ All quality checks passed
|
|
run: |
|
|
echo "✅ All code quality checks passed successfully!"
|
|
echo " - ESLint: No linting errors"
|
|
echo " - Prettier: Code is properly formatted"
|
|
echo " - TypeScript: No type errors"
|
|
|
|
# ============================================
|
|
# Job 2: Build and Push Docker Image
|
|
# ============================================
|
|
build-and-push:
|
|
name: 🏗️ Build and Push Docker Image
|
|
runs-on: ubuntu-latest
|
|
needs: [lint] # Wait for lint job to complete successfully
|
|
|
|
steps:
|
|
- name: 🔎 Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: 🔐 Log in to Docker Registry (if credentials provided)
|
|
run: |
|
|
if [ -n "${{ secrets.REGISTRY_USERNAME }}" ] && [ -n "${{ secrets.REGISTRY_PASSWORD }}" ]; then
|
|
echo "Logging into ${{ env.REGISTRY }} with credentials..."
|
|
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin
|
|
echo "✅ Login successful"
|
|
else
|
|
echo "⚠️ No registry credentials provided - using insecure/public registry"
|
|
fi
|
|
|
|
- name: 🏗️ Build Docker image
|
|
timeout-minutes: 30
|
|
env:
|
|
DOCKER_BUILDKIT: 1 # Enable BuildKit for faster builds and better caching
|
|
run: |
|
|
echo "Building Next.js Docker image with BuildKit..."
|
|
echo "Build context size:"
|
|
du -sh . 2>/dev/null || echo "Cannot measure context size"
|
|
|
|
# Build the Docker image
|
|
# - Uses Dockerfile.nextjs from project root
|
|
# - Tags image with both 'latest' and commit SHA
|
|
# - Enables inline cache for faster subsequent builds
|
|
# -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} ❗ do this if deploying on PR creation
|
|
docker build \
|
|
--progress=plain \
|
|
--build-arg BUILDKIT_INLINE_CACHE=1 \
|
|
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest \
|
|
-f Dockerfile.nextjs \
|
|
.
|
|
|
|
echo "✅ Build successful"
|
|
echo "Image size:"
|
|
docker images ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
|
|
|
- name: 🚀 Push Docker image to registry
|
|
run: |
|
|
echo "Pushing image to registry..."
|
|
|
|
# Push both tags (latest and commit SHA)
|
|
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
|
# docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
|
|
|
|
echo "✅ Image pushed successfully"
|
|
echo " - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest"
|
|
# echo " - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}"
|
|
|
|
# ============================================
|
|
# Job 2: Deploy to Production Server
|
|
# ============================================
|
|
deploy-production:
|
|
name: 🚀 Deploy to Production
|
|
runs-on: ubuntu-latest
|
|
needs: [build-and-push] # Wait for build job to complete
|
|
environment:
|
|
name: production
|
|
url: http://192.168.1.54:3030 # Update with your actual production URL
|
|
|
|
steps:
|
|
- name: 🔎 Checkout code (for docker-compose file)
|
|
uses: actions/checkout@v4
|
|
|
|
- name: 🔐 Validate Registry Access on Production Server
|
|
uses: appleboy/ssh-action@v1.0.3
|
|
env:
|
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
|
REGISTRY_URL: ${{ env.REGISTRY }}
|
|
with:
|
|
host: ${{ vars.PRODUCTION_HOST }}
|
|
username: ${{ vars.PRODUCTION_USER }}
|
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
port: 22
|
|
envs: REGISTRY_PASSWORD,REGISTRY_USERNAME,REGISTRY_URL
|
|
script: |
|
|
echo "=== Validating Docker Registry access ==="
|
|
if [ -n "$REGISTRY_USERNAME" ] && [ -n "$REGISTRY_PASSWORD" ]; then
|
|
echo "Logging into $REGISTRY_URL with credentials..."
|
|
echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY_URL" -u "$REGISTRY_USERNAME" --password-stdin
|
|
echo "✅ Registry authentication successful"
|
|
else
|
|
echo "⚠️ No registry credentials - using insecure/public registry"
|
|
echo "Testing registry connectivity..."
|
|
curl -f "http://$REGISTRY_URL/v2/" || { echo "❌ Registry not accessible"; exit 1; }
|
|
echo "✅ Registry is accessible"
|
|
fi
|
|
|
|
- name: 📁 Ensure application directory structure
|
|
uses: appleboy/ssh-action@v1.0.3
|
|
with:
|
|
host: ${{ vars.PRODUCTION_HOST }}
|
|
username: ${{ vars.PRODUCTION_USER }}
|
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
port: 22
|
|
script: |
|
|
echo "=== Ensuring application directory structure ==="
|
|
|
|
# Verify base directory exists and is writable
|
|
# Update /opt/mypage to match your deployment directory
|
|
if [ ! -d /opt/mypage ]; then
|
|
echo "❌ /opt/mypage does not exist!"
|
|
echo "Please run manually on production server:"
|
|
echo " sudo mkdir -p /opt/mypage"
|
|
echo " sudo chown -R deployer:docker /opt/mypage"
|
|
echo " sudo chmod -R 775 /opt/mypage"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -w /opt/mypage ]; then
|
|
echo "❌ /opt/mypage is not writable by $USER user"
|
|
echo "Please run manually on production server:"
|
|
echo " sudo chown -R deployer:docker /opt/mypage"
|
|
echo " sudo chmod -R 775 /opt/mypage"
|
|
exit 1
|
|
fi
|
|
|
|
# Create data directories for logs
|
|
mkdir -p /opt/mypage/data/logs || { echo "❌ Failed to create logs directory"; exit 1; }
|
|
|
|
echo "✅ Directory structure ready"
|
|
ls -la /opt/mypage
|
|
|
|
- name: 📦 Copy docker-compose.prod.yml to server
|
|
uses: appleboy/scp-action@v0.1.7
|
|
with:
|
|
host: ${{ vars.PRODUCTION_HOST }}
|
|
username: ${{ vars.PRODUCTION_USER }}
|
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
port: 22
|
|
source: "docker-compose.prod.yml"
|
|
target: "/opt/mypage/"
|
|
overwrite: true
|
|
|
|
- name: 🐳 Deploy application via Docker Compose
|
|
uses: appleboy/ssh-action@v1.0.3
|
|
env:
|
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD || '' }}
|
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME || '' }}
|
|
REGISTRY_URL: ${{ env.REGISTRY }}
|
|
IMAGE_FULL: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
|
with:
|
|
host: ${{ vars.PRODUCTION_HOST }}
|
|
username: ${{ vars.PRODUCTION_USER }}
|
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
port: 22
|
|
envs: REGISTRY_PASSWORD,REGISTRY_USERNAME,REGISTRY_URL,IMAGE_FULL
|
|
script_stop: true # Stop execution on any error
|
|
script: |
|
|
echo "=== Starting deployment to production server ==="
|
|
cd /opt/mypage
|
|
|
|
# Log in to Docker registry (if credentials are configured)
|
|
if [ -n "$REGISTRY_USERNAME" ] && [ -n "$REGISTRY_PASSWORD" ]; then
|
|
echo "=== Logging in to Docker registry ==="
|
|
echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY_URL" -u "$REGISTRY_USERNAME" --password-stdin
|
|
echo "✅ Registry login successful"
|
|
else
|
|
echo "⚠️ No registry credentials - using insecure/public registry (no login required)"
|
|
fi
|
|
|
|
# Pull latest image from registry
|
|
echo "=== Pulling latest Docker image ==="
|
|
docker pull "$IMAGE_FULL"
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo "❌ Failed to pull image, aborting deployment"
|
|
exit 1
|
|
fi
|
|
|
|
# Deploy new container
|
|
# - Stops old container
|
|
# - Removes old container
|
|
# - Creates and starts new container with fresh image
|
|
echo "=== Deploying new container ==="
|
|
docker compose -f docker-compose.prod.yml up -d --force-recreate
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo "❌ Failed to deploy new container"
|
|
echo "Check logs above for errors"
|
|
exit 1
|
|
fi
|
|
|
|
# Check container status
|
|
echo "=== Container Status ==="
|
|
docker compose -f docker-compose.prod.yml ps
|
|
|
|
# Show recent logs for debugging
|
|
echo "=== Recent application logs ==="
|
|
docker compose -f docker-compose.prod.yml logs --tail=50
|
|
|
|
# Clean up old/unused images to save disk space
|
|
echo "=== Cleaning up old Docker images ==="
|
|
docker image prune -f
|
|
|
|
echo "✅ Deployment completed successfully ==="
|
|
|
|
- name: ❤️ Health check
|
|
uses: appleboy/ssh-action@v1.0.3
|
|
with:
|
|
host: ${{ vars.PRODUCTION_HOST }}
|
|
username: ${{ vars.PRODUCTION_USER }}
|
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
port: 22
|
|
script: |
|
|
echo "=== Performing health check ==="
|
|
cd /opt/mypage
|
|
max_attempts=15
|
|
attempt=1
|
|
|
|
# Wait for container to be healthy (respect start_period from health check)
|
|
echo "Waiting for application to start (40s start period)..."
|
|
sleep 40
|
|
|
|
# Retry health check up to 15 times
|
|
while [ $attempt -le $max_attempts ]; do
|
|
# Check if application responds at port 3030
|
|
if curl -f http://localhost:3030/ > /dev/null 2>&1; then
|
|
echo "✅ Health check passed!"
|
|
echo "Application is healthy and responding to requests"
|
|
exit 0
|
|
fi
|
|
echo "Attempt $attempt/$max_attempts: Health check failed, retrying in 5s..."
|
|
sleep 5
|
|
attempt=$((attempt + 1))
|
|
done
|
|
|
|
# Health check failed - gather diagnostic information
|
|
echo "❌ Health check failed after $max_attempts attempts"
|
|
echo ""
|
|
echo "=== Container Status ==="
|
|
docker compose -f docker-compose.prod.yml ps
|
|
echo ""
|
|
echo "=== Container Health ==="
|
|
docker inspect mypage-prod --format='{{.State.Health.Status}}' 2>/dev/null || echo "No health status"
|
|
echo ""
|
|
echo "=== Recent Application Logs ==="
|
|
docker compose -f docker-compose.prod.yml logs --tail=100
|
|
|
|
exit 1
|
|
|
|
- name: 📊 Deployment summary
|
|
if: always() # Run even if previous steps fail
|
|
run: |
|
|
echo "### 🚀 Deployment Summary" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Environment**: Production" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Image**: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Commit**: \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Workflow Run**: #${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Triggered By**: ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Status**: ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Next Steps:**" >> $GITHUB_STEP_SUMMARY
|
|
echo "1. Verify application is accessible at production URL" >> $GITHUB_STEP_SUMMARY
|
|
echo "2. Check application logs for any errors" >> $GITHUB_STEP_SUMMARY
|
|
echo "3. Monitor resource usage and performance" >> $GITHUB_STEP_SUMMARY
|