refactor: add production deploy to combined CI/CD workflow
All 3 jobs in one file: - test-build: lint, test, build (all branches) - deploy-dev: Docker + deploy (dev only, needs test-build) - deploy-production: Docker + deploy (production only, needs test-build) Removes separate production-deploy.yml. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -156,3 +156,118 @@ jobs:
|
||||
|
||||
- name: Cleanup
|
||||
run: docker image prune -f
|
||||
|
||||
# ── Job 3: Deploy to production (only on production branch, after tests pass) ──
|
||||
deploy-production:
|
||||
needs: test-build
|
||||
if: github.ref == 'refs/heads/production' && github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
echo "🏗️ Building production Docker image..."
|
||||
DOCKER_BUILDKIT=1 docker build \
|
||||
--cache-from ${{ env.DOCKER_IMAGE }}:production \
|
||||
--cache-from ${{ env.DOCKER_IMAGE }}:latest \
|
||||
-t ${{ env.DOCKER_IMAGE }}:production \
|
||||
-t ${{ env.DOCKER_IMAGE }}:latest \
|
||||
.
|
||||
echo "✅ Docker image built successfully"
|
||||
|
||||
- name: Deploy production container
|
||||
run: |
|
||||
echo "🚀 Starting production deployment..."
|
||||
|
||||
COMPOSE_FILE="docker-compose.production.yml"
|
||||
CONTAINER_NAME="portfolio-app"
|
||||
HEALTH_PORT="3000"
|
||||
|
||||
# Backup current container ID
|
||||
OLD_CONTAINER=$(docker ps -q -f "name=^/${CONTAINER_NAME}$" || echo "")
|
||||
|
||||
# Ensure network exists
|
||||
docker network create portfolio_net 2>/dev/null || true
|
||||
|
||||
# Export variables for docker-compose
|
||||
export N8N_WEBHOOK_URL="${N8N_WEBHOOK_URL}"
|
||||
export N8N_SECRET_TOKEN="${N8N_SECRET_TOKEN}"
|
||||
export N8N_API_KEY="${N8N_API_KEY}"
|
||||
export MY_EMAIL="${MY_EMAIL}"
|
||||
export MY_INFO_EMAIL="${MY_INFO_EMAIL}"
|
||||
export MY_PASSWORD="${MY_PASSWORD}"
|
||||
export MY_INFO_PASSWORD="${MY_INFO_PASSWORD}"
|
||||
export ADMIN_BASIC_AUTH="${ADMIN_BASIC_AUTH}"
|
||||
export ADMIN_SESSION_SECRET="${ADMIN_SESSION_SECRET}"
|
||||
export DIRECTUS_URL="${DIRECTUS_URL}"
|
||||
export DIRECTUS_STATIC_TOKEN="${DIRECTUS_STATIC_TOKEN}"
|
||||
|
||||
# Start new container via compose
|
||||
echo "🆕 Starting new production container..."
|
||||
docker compose -f $COMPOSE_FILE up -d portfolio
|
||||
|
||||
# Wait for health
|
||||
echo "⏳ Waiting for container to be healthy..."
|
||||
HEALTH_CHECK_PASSED=false
|
||||
for i in {1..90}; do
|
||||
NEW_CONTAINER=$(docker compose -f $COMPOSE_FILE ps -q portfolio 2>/dev/null | head -1)
|
||||
if [ -z "$NEW_CONTAINER" ]; then
|
||||
NEW_CONTAINER=$(docker ps -q -f "name=^/${CONTAINER_NAME}$")
|
||||
fi
|
||||
if [ ! -z "$NEW_CONTAINER" ]; then
|
||||
HEALTH=$(docker inspect $NEW_CONTAINER --format='{{.State.Health.Status}}' 2>/dev/null || echo "starting")
|
||||
if [ "$HEALTH" == "healthy" ]; then
|
||||
echo "✅ Production container is healthy!"
|
||||
HEALTH_CHECK_PASSED=true
|
||||
break
|
||||
fi
|
||||
if curl -f -s --max-time 2 http://localhost:$HEALTH_PORT/api/health > /dev/null 2>&1; then
|
||||
echo "✅ Production HTTP health check passed!"
|
||||
HEALTH_CHECK_PASSED=true
|
||||
break
|
||||
fi
|
||||
fi
|
||||
if [ $((i % 15)) -eq 0 ]; then
|
||||
echo "📊 Health: ${HEALTH:-unknown} (attempt $i/90)"
|
||||
docker compose -f $COMPOSE_FILE logs --tail=5 portfolio 2>/dev/null || true
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ "$HEALTH_CHECK_PASSED" != "true" ]; then
|
||||
echo "❌ Production health check failed!"
|
||||
docker compose -f $COMPOSE_FILE logs --tail=50 portfolio 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove old container if different
|
||||
if [ ! -z "$OLD_CONTAINER" ]; then
|
||||
NEW_CONTAINER=$(docker ps -q -f "name=^/${CONTAINER_NAME}$")
|
||||
if [ ! -z "$NEW_CONTAINER" ] && [ "$OLD_CONTAINER" != "$NEW_CONTAINER" ]; then
|
||||
echo "🧹 Removing old container..."
|
||||
docker stop $OLD_CONTAINER 2>/dev/null || true
|
||||
docker rm $OLD_CONTAINER 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "✅ Production deployment completed!"
|
||||
env:
|
||||
NODE_ENV: production
|
||||
LOG_LEVEL: ${{ vars.LOG_LEVEL || 'info' }}
|
||||
NEXT_PUBLIC_BASE_URL: ${{ vars.NEXT_PUBLIC_BASE_URL_PRODUCTION || 'https://dk0.dev' }}
|
||||
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 }}
|
||||
ADMIN_SESSION_SECRET: ${{ secrets.ADMIN_SESSION_SECRET }}
|
||||
N8N_WEBHOOK_URL: ${{ vars.N8N_WEBHOOK_URL || '' }}
|
||||
N8N_SECRET_TOKEN: ${{ secrets.N8N_SECRET_TOKEN || '' }}
|
||||
N8N_API_KEY: ${{ vars.N8N_API_KEY || '' }}
|
||||
DIRECTUS_URL: ${{ vars.DIRECTUS_URL || 'https://cms.dk0.dev' }}
|
||||
DIRECTUS_STATIC_TOKEN: ${{ secrets.DIRECTUS_STATIC_TOKEN || '' }}
|
||||
|
||||
- name: Cleanup
|
||||
run: docker image prune -f
|
||||
|
||||
Reference in New Issue
Block a user