Files
Randall/.github/workflows/ci.yml
2026-03-24 21:08:14 +01:00

138 lines
4.6 KiB
YAML

name: CI / CD
run-name: "${{ github.event_name == 'pull_request' && format('PR #{0} — {1}', github.event.pull_request.number, github.event.pull_request.title) || format('merge to {0} by @{1}', github.ref_name, github.actor) }}"
on:
push:
branches: [main]
pull_request:
jobs:
# ── E2E Tests ──────────────────────────────────────────────────────────────
e2e:
name: E2E Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build images
run: docker compose -f cicd/docker/docker-compose.yml build
- name: Start services
run: docker compose -f cicd/docker/docker-compose.yml up -d
env:
JWT_KEY: ${{ secrets.JWT_KEY }}
- name: Wait for frontend to be ready
run: |
echo "Waiting for services to become healthy..."
timeout 120 bash -c \
'until curl -sf http://localhost > /dev/null; do echo " still waiting..."; sleep 3; done'
echo "Services are ready."
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: tests/e2e/package-lock.json
- name: Install dependencies
working-directory: tests/e2e
run: npm ci
- name: Install Playwright browsers
working-directory: tests/e2e
run: npx playwright install --with-deps chromium
- name: Run E2E tests
working-directory: tests/e2e
env:
BASE_URL: http://localhost
CI: 'true'
run: npx playwright test
- name: Upload Playwright report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: tests/e2e/playwright-report/
retention-days: 30
- name: Dump Docker logs on failure
if: failure()
run: docker compose -f cicd/docker/docker-compose.yml logs
- name: Stop services
if: always()
run: docker compose -f cicd/docker/docker-compose.yml down -v
# ── Deploy ─────────────────────────────────────────────────────────────────
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: e2e
# Only deploy on merges to main, not on pull requests
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan -H "${{ secrets.DEPLOY_HOST }}" >> ~/.ssh/known_hosts
- name: Sync source to VM
run: |
rsync -az --delete \
-e "ssh -i ~/.ssh/deploy_key" \
--exclude='.git/' \
--exclude='.github/' \
--exclude='tests/' \
--exclude='src/frontend/node_modules/' \
--exclude='src/frontend/dist/' \
--exclude='src/backend/**/bin/' \
--exclude='src/backend/**/obj/' \
--exclude='src/backend/**/*.db' \
--exclude='src/backend/**/*.db-shm' \
--exclude='src/backend/**/*.db-wal' \
./ ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PATH }}/
- name: Deploy with Docker Compose
uses: appleboy/ssh-action@v1.0.3
env:
JWT_KEY: ${{ secrets.JWT_KEY }}
PORT: ${{ secrets.DEPLOY_PORT || '80' }}
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
envs: JWT_KEY,PORT
script: |
set -e
cd "${{ secrets.DEPLOY_PATH }}"
echo "Building backend..."
DOCKER_BUILDKIT=0 docker compose -f cicd/docker/docker-compose.yml build backend
echo "Building frontend..."
DOCKER_BUILDKIT=0 docker compose -f cicd/docker/docker-compose.yml build frontend
echo "Starting services..."
docker compose -f cicd/docker/docker-compose.yml up -d
echo "Removing unused images..."
docker image prune -f
echo "Running containers:"
docker compose -f cicd/docker/docker-compose.yml ps