frappe_docker/.github/workflows/deploy.yml
2025-07-18 13:42:09 +03:00

212 lines
6.3 KiB
YAML

name: Deploy Academy LMS to Hetzner
on:
# Manual trigger
workflow_dispatch:
inputs:
force_rebuild:
description: 'Force rebuild all images'
required: false
default: 'false'
type: boolean
environment:
description: 'Deployment environment'
required: false
default: 'test'
type: choice
options:
- production
- test
# Webhook triggers from watched repositories
repository_dispatch:
types: [academy-lms-updated, academy-ai-tutor-updated, academy-langchain-updated]
# Push to master branch of this repo
push:
branches: [ master ]
paths:
- 'compose.yaml'
- 'images/**'
- '.github/workflows/**'
- 'nginx/**'
env:
REGISTRY: ghcr.io
HETZNER_HOST: 188.245.211.114
HETZNER_USER: ignis_academy_lms
DEPLOY_PATH: /opt/frappe-deployment
jobs:
build-and-deploy:
runs-on: ubuntu-latest
# Use environment for secrets management
environment: ${{ github.event.inputs.environment || 'test' }}
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 Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for Frappe image
id: meta-frappe
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/exarlabs/ignis-academy-lms
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Frappe image
uses: docker/build-push-action@v5
with:
context: .
file: ./images/custom/Containerfile
push: true
tags: ${{ steps.meta-frappe.outputs.tags }}
labels: ${{ steps.meta-frappe.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
LMS_REPO_URL=https://github.com/ExarLabs/academy-lms
AI_TUTOR_REPO_URL=https://github.com/ExarLabs/academy-ai-tutor-chat
- name: Clone and prepare LangChain service
run: |
# Clone private repository using PAT (Personal Access Token)
git clone https://${{ secrets.ACADEMY_DOCKER_PAT }}@github.com/ExarLabs/academy-LangChain.git langchain-temp
- name: Extract metadata for LangChain image
id: meta-langchain
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/exarlabs/academy-langchain
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push LangChain image
uses: docker/build-push-action@v5
with:
context: ./langchain-temp
push: true
tags: ${{ steps.meta-langchain.outputs.tags }}
labels: ${{ steps.meta-langchain.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Setup SSH key
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.HETZNER_SSH_KEY }}
- name: Add Hetzner server to known hosts
run: |
ssh-keyscan -H ${{ env.HETZNER_HOST }} >> ~/.ssh/known_hosts
- name: Deploy to Hetzner
run: |
# Copy deployment files to server
scp -r compose.yaml nginx/ scripts/ .env.example ${{ env.HETZNER_USER }}@${{ env.HETZNER_HOST }}:${{ env.DEPLOY_PATH }}/
# Copy environment file if it doesn't exist
ssh ${{ env.HETZNER_USER }}@${{ env.HETZNER_HOST }} "
cd ${{ env.DEPLOY_PATH }}
if [ ! -f .env ]; then
cp .env.example .env
echo 'Please update .env file with your configuration'
fi
"
# Login to private registry
ssh ${{ env.HETZNER_USER }}@${{ env.HETZNER_HOST }} "
echo '${{ secrets.GITHUB_TOKEN }}' | docker login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin
"
- name: Pull latest images
run: |
ssh ${{ env.HETZNER_USER }}@${{ env.HETZNER_HOST }} "
cd ${{ env.DEPLOY_PATH }}
docker compose pull
"
- name: Stop services
run: |
ssh ${{ env.HETZNER_USER }}@${{ env.HETZNER_HOST }} "
cd ${{ env.DEPLOY_PATH }}
docker compose down --timeout 30
"
- name: Start services
run: |
ssh ${{ env.HETZNER_USER }}@${{ env.HETZNER_HOST }} "
cd ${{ env.DEPLOY_PATH }}
docker compose up -d
"
- name: Wait for services to be ready
run: |
echo "Waiting for services to start..."
sleep 30
- name: Run migrations
run: |
ssh ${{ env.HETZNER_USER }}@${{ env.HETZNER_HOST }} "
cd ${{ env.DEPLOY_PATH }}
# Make sure the script is executable
chmod +x scripts/*
./scripts/migrate-all-sites.sh
"
- name: Check service status
run: |
ssh ${{ env.HETZNER_USER }}@${{ env.HETZNER_HOST }} "
cd ${{ env.DEPLOY_PATH }}
docker compose ps
"
- name: Health check
run: |
# Wait a bit more for services to fully start
sleep 60
# Check if all docker container are up and running
ssh ${{ env.HETZNER_USER }}@${{ env.HETZNER_HOST }} "
count=$(docker container ls --format "{{.Status}}" | grep -v "^Up" | wc -l)
if [ $count -gt 0 ]; then
echo "❌ $count containers are not running properly"
cd ${{ env.DEPLOY_PATH }}
docker compose logs --tail=50
exit 1
else
echo "✅ All containers are running"
fi
"
- name: Notify deployment status
if: always()
run: |
if [ "${{ job.status }}" == "success" ]; then
echo "🚀 Deployment to Hetzner completed successfully!"
echo "🌐 Access your application at: http://${{ env.HETZNER_HOST }}"
else
echo "💥 Deployment failed. Check the logs above for details."
fi