Skip to content

I want to know when bad things happen without wasting money on comple… #38

I want to know when bad things happen without wasting money on comple…

I want to know when bad things happen without wasting money on comple… #38

Workflow file for this run

name: Deploy to Production
on:
push:
branches:
- main
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
needs: build-and-push
runs-on: ubuntu-latest
environment: prod
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check required secrets
run: |
if [ -z "${{ secrets.HETZNER_HOST }}" ]; then
echo "Error: HETZNER_HOST secret is not set"
exit 1
fi
if [ -z "${{ secrets.HETZNER_USERNAME }}" ]; then
echo "Error: HETZNER_USERNAME secret is not set"
exit 1
fi
if [ -z "${{ secrets.HETZNER_SSH_KEY }}" ]; then
echo "Error: HETZNER_SSH_KEY secret is not set"
exit 1
fi
- name: Deploy to Hetzner
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HETZNER_HOST }}
username: ${{ secrets.HETZNER_USERNAME }}
key: ${{ secrets.HETZNER_SSH_KEY }}
script: |
# Navigate to app directory
cd ~/app
# Pull latest changes from git
if [ -d .git ]; then
git pull
else
git clone https://github.com/${{ github.repository }}.git .
fi
# Export environment variables for docker-compose
export REGISTRY="${{ env.REGISTRY }}"
export IMAGE_NAME="${{ env.IMAGE_NAME }}"
# Login to GitHub Container Registry
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
# Pull the latest image
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
# Stop and remove existing containers
docker-compose -f docker-compose.yml -f docker-compose.prod.yml down
# Start new containers with production configuration
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Clean up unused images
docker image prune -f
# Function to check container health
check_container_health() {
local container_name=$1
local status=$(docker inspect --format='{{.State.Status}}' $container_name)
echo "Container $container_name status: $status"
[ "$status" = "running" ]
return $?
}
# Function to check service availability
check_service() {
local attempt=$1
local max_attempts=$2
echo "Attempt $attempt/$max_attempts - Checking service health..."
# Check container health first
if ! check_container_health "app-app-1"; then
echo "App container not running"
docker-compose -f docker-compose.yml -f docker-compose.prod.yml logs --tail=50 app
return 1
fi
if ! check_container_health "app-nginx-1"; then
echo "Nginx container not running"
docker-compose -f docker-compose.yml -f docker-compose.prod.yml logs --tail=50 nginx
return 1
fi
# Check HTTP response using Docker network
# Use curl inside the nginx container to check the app
local http_code=$(docker exec app-nginx-1 curl -s -o /dev/null -w "%{http_code}" http://app:4321)
if [ "$http_code" != "200" ] && [ "$http_code" != "301" ] && [ "$http_code" != "308" ]; then
echo "HTTP check failed with status $http_code"
return 1
fi
echo "✅ Basic health check passed"
return 0
}
# Initial delay to let containers start
echo "Waiting 45 seconds for containers to fully start..."
sleep 45
# Verify deployment with improved checks
echo "Starting deployment verification..."
max_attempts=4
attempt=1
while [ $attempt -le $max_attempts ]; do
if check_service $attempt $max_attempts; then
echo "✅ Deployment verification successful!"
exit 0
fi
# If we're at attempt 2, dump all container logs
if [ $attempt -eq 2 ]; then
echo "🔍 Mid-verification status check:"
docker-compose -f docker-compose.yml -f docker-compose.prod.yml ps
echo "📋 Container logs:"
docker-compose -f docker-compose.yml -f docker-compose.prod.yml logs --tail=100
fi
echo "⏳ Waiting 30 seconds before next attempt..."
sleep 30
attempt=$((attempt + 1))
done
echo "❌ Deployment verification failed after $max_attempts attempts"
echo "📊 Final system status:"
docker-compose -f docker-compose.yml -f docker-compose.prod.yml ps
echo "📋 Final logs:"
docker-compose -f docker-compose.yml -f docker-compose.prod.yml logs
exit 1