name: Dev Deployment (Zero Downtime) on: push: branches: [ dev ] env: NODE_VERSION: '20' DOCKER_IMAGE: portfolio-app IMAGE_TAG: staging jobs: deploy-dev: 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: Build Docker image run: | echo "๐Ÿ—๏ธ Building dev Docker image..." docker build -t ${{ env.DOCKER_IMAGE }}:${{ env.IMAGE_TAG }} . echo "โœ… Docker image built successfully" - name: Zero-Downtime Dev Deployment run: | echo "๐Ÿš€ Starting zero-downtime dev deployment..." COMPOSE_FILE="docker-compose.staging.yml" CONTAINER_NAME="portfolio-app-staging" HEALTH_PORT="3002" # Backup current container ID if running OLD_CONTAINER=$(docker ps -q -f name=$CONTAINER_NAME || echo "") # Start new container with updated image echo "๐Ÿ†• Starting new dev container..." docker compose -f $COMPOSE_FILE up -d --no-deps --build portfolio-staging # Wait for new container to be healthy echo "โณ Waiting for new container to be healthy..." for i in {1..60}; do NEW_CONTAINER=$(docker ps -q -f name=$CONTAINER_NAME) if [ ! -z "$NEW_CONTAINER" ]; then # Check health status HEALTH=$(docker inspect $NEW_CONTAINER --format='{{.State.Health.Status}}' 2>/dev/null || echo "starting") if [ "$HEALTH" == "healthy" ]; then echo "โœ… New container is healthy!" break fi # Also check HTTP health endpoint if curl -f http://localhost:$HEALTH_PORT/api/health > /dev/null 2>&1; then echo "โœ… New container is responding!" break fi fi echo "โณ Waiting... ($i/60)" sleep 2 done # Verify new container is working if ! curl -f http://localhost:$HEALTH_PORT/api/health > /dev/null 2>&1; then echo "โš ๏ธ New dev container health check failed, but continuing (non-blocking)..." docker compose -f $COMPOSE_FILE logs --tail=50 portfolio-staging fi # Remove old container if it exists and is different if [ ! -z "$OLD_CONTAINER" ]; then NEW_CONTAINER=$(docker ps -q -f name=$CONTAINER_NAME) if [ "$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 "โœ… Dev deployment completed!" env: NODE_ENV: staging LOG_LEVEL: ${{ vars.LOG_LEVEL || 'debug' }} NEXT_PUBLIC_BASE_URL: ${{ vars.NEXT_PUBLIC_BASE_URL || 'https://dev.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 }} N8N_WEBHOOK_URL: ${{ vars.N8N_WEBHOOK_URL || '' }} N8N_SECRET_TOKEN: ${{ secrets.N8N_SECRET_TOKEN || '' }} - name: Dev Health Check run: | echo "๐Ÿ” Running dev health checks..." for i in {1..20}; do if curl -f http://localhost:3002/api/health && curl -f http://localhost:3002/ > /dev/null; then echo "โœ… Dev is fully operational!" exit 0 fi echo "โณ Waiting for dev... ($i/20)" sleep 3 done echo "โš ๏ธ Dev health check failed, but continuing (non-blocking)..." docker compose -f docker-compose.staging.yml logs --tail=50 - name: Cleanup run: | echo "๐Ÿงน Cleaning up old images..." docker image prune -f echo "โœ… Cleanup completed"