diff --git a/.github/workflows/cd-macmini.yml b/.github/workflows/cd-macmini.yml new file mode 100644 index 000000000..bba71caf2 --- /dev/null +++ b/.github/workflows/cd-macmini.yml @@ -0,0 +1,247 @@ +# CD Pipeline: Auto-deploy to Mac Mini on push to main +# +# Requires a self-hosted GitHub Actions runner on the Mac Mini. +# Setup: see docs/MAC_MINI_RUNNER_SETUP.md +# +# Flow: +# Push → main : Detects changed services → rebuilds & restarts only those containers +# +# The runner executes directly on the Mac Mini, so it has access to +# Docker, docker-compose, and the local project directory. + +name: CD Mac Mini + +on: + push: + branches: + - main + workflow_dispatch: + inputs: + service: + description: 'Service to deploy (or "all" for everything)' + required: false + default: 'all' + type: choice + options: + - all + - matrix-web + - mana-core-auth + - chat-backend + - chat-web + - todo-backend + - todo-web + - calendar-backend + - calendar-web + - clock-backend + - clock-web + - contacts-backend + - contacts-web + - matrix-mana-bot + +concurrency: + group: cd-macmini + cancel-in-progress: false # Don't cancel in-progress deploys + +env: + PROJECT_DIR: /Users/till/projects/manacore-monorepo + COMPOSE_FILE: docker-compose.macmini.yml + ENV_FILE: .env.macmini + +jobs: + # =========================================== + # Detect what changed + # =========================================== + detect-changes: + name: Detect Changes + runs-on: self-hosted + if: github.event_name == 'push' + outputs: + matrix-web: ${{ steps.changes.outputs.matrix-web }} + mana-core-auth: ${{ steps.changes.outputs.mana-core-auth }} + chat-backend: ${{ steps.changes.outputs.chat-backend }} + chat-web: ${{ steps.changes.outputs.chat-web }} + todo-backend: ${{ steps.changes.outputs.todo-backend }} + todo-web: ${{ steps.changes.outputs.todo-web }} + calendar-backend: ${{ steps.changes.outputs.calendar-backend }} + calendar-web: ${{ steps.changes.outputs.calendar-web }} + clock-backend: ${{ steps.changes.outputs.clock-backend }} + clock-web: ${{ steps.changes.outputs.clock-web }} + contacts-backend: ${{ steps.changes.outputs.contacts-backend }} + contacts-web: ${{ steps.changes.outputs.contacts-web }} + matrix-mana-bot: ${{ steps.changes.outputs.matrix-mana-bot }} + any-changes: ${{ steps.changes.outputs.any-changes }} + steps: + - name: Check for changes + id: changes + run: | + cd "${{ env.PROJECT_DIR }}" + + # Get changed files between previous and current commit + CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "") + + # Shared packages trigger rebuilds for all services that use them + SHARED_CHANGED="false" + if echo "$CHANGED" | grep -qE "^packages/(shared-ui|shared-theme|shared-icons|shared-tailwind|shared-auth|shared-branding|shared-i18n|shared-utils|shared-types)/"; then + SHARED_CHANGED="true" + fi + + check_changes() { + local name=$1 + shift + local result="false" + for path in "$@"; do + if echo "$CHANGED" | grep -q "^$path"; then + result="true" + break + fi + done + # Shared package changes trigger rebuild + if [ "$SHARED_CHANGED" == "true" ]; then + result="true" + fi + echo "$name=$result" >> $GITHUB_OUTPUT + echo " $name: $result" + } + + echo "Changed files:" + echo "$CHANGED" | head -20 + echo "" + echo "Shared packages changed: $SHARED_CHANGED" + echo "" + + check_changes "matrix-web" "apps/matrix/apps/web/" "apps/matrix/packages/" + check_changes "mana-core-auth" "services/mana-core-auth/" + check_changes "chat-backend" "apps/chat/apps/backend/" "apps/chat/packages/" + check_changes "chat-web" "apps/chat/apps/web/" "apps/chat/packages/" + check_changes "todo-backend" "apps/todo/apps/backend/" "apps/todo/packages/" + check_changes "todo-web" "apps/todo/apps/web/" "apps/todo/packages/" + check_changes "calendar-backend" "apps/calendar/apps/backend/" "apps/calendar/packages/" + check_changes "calendar-web" "apps/calendar/apps/web/" "apps/calendar/packages/" + check_changes "clock-backend" "apps/clock/apps/backend/" "apps/clock/packages/" + check_changes "clock-web" "apps/clock/apps/web/" "apps/clock/packages/" + check_changes "contacts-backend" "apps/contacts/apps/backend/" "apps/contacts/packages/" + check_changes "contacts-web" "apps/contacts/apps/web/" "apps/contacts/packages/" + check_changes "matrix-mana-bot" "services/matrix-mana-bot/" "packages/matrix-bot-common/" + + # Check if anything needs deploying + ANY="false" + for svc in matrix-web mana-core-auth chat-backend chat-web todo-backend todo-web calendar-backend calendar-web clock-backend clock-web contacts-backend contacts-web matrix-mana-bot; do + val=$(grep "^$svc=" $GITHUB_OUTPUT | tail -1 | cut -d= -f2) + if [ "$val" == "true" ]; then + ANY="true" + break + fi + done + echo "any-changes=$ANY" >> $GITHUB_OUTPUT + + # =========================================== + # Deploy changed services + # =========================================== + deploy: + name: Deploy + runs-on: self-hosted + needs: [detect-changes] + if: | + always() && + (needs.detect-changes.result == 'success' && needs.detect-changes.outputs.any-changes == 'true') || + github.event_name == 'workflow_dispatch' + steps: + - name: Pull latest code + run: | + cd "${{ env.PROJECT_DIR }}" + git pull origin main + + - name: Determine services to deploy + id: services + run: | + cd "${{ env.PROJECT_DIR }}" + SERVICES="" + + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + INPUT="${{ inputs.service }}" + if [ "$INPUT" == "all" ]; then + echo "Manual deploy: all services" + echo "deploy-all=true" >> $GITHUB_OUTPUT + exit 0 + else + SERVICES="$INPUT" + fi + else + # Build list from detected changes + if [ "${{ needs.detect-changes.outputs.matrix-web }}" == "true" ]; then SERVICES="$SERVICES matrix-web"; fi + if [ "${{ needs.detect-changes.outputs.mana-core-auth }}" == "true" ]; then SERVICES="$SERVICES mana-auth"; fi + if [ "${{ needs.detect-changes.outputs.chat-backend }}" == "true" ]; then SERVICES="$SERVICES chat-backend"; fi + if [ "${{ needs.detect-changes.outputs.chat-web }}" == "true" ]; then SERVICES="$SERVICES chat-web"; fi + if [ "${{ needs.detect-changes.outputs.todo-backend }}" == "true" ]; then SERVICES="$SERVICES todo-backend"; fi + if [ "${{ needs.detect-changes.outputs.todo-web }}" == "true" ]; then SERVICES="$SERVICES todo-web"; fi + if [ "${{ needs.detect-changes.outputs.calendar-backend }}" == "true" ]; then SERVICES="$SERVICES calendar-backend"; fi + if [ "${{ needs.detect-changes.outputs.calendar-web }}" == "true" ]; then SERVICES="$SERVICES calendar-web"; fi + if [ "${{ needs.detect-changes.outputs.clock-backend }}" == "true" ]; then SERVICES="$SERVICES clock-backend"; fi + if [ "${{ needs.detect-changes.outputs.clock-web }}" == "true" ]; then SERVICES="$SERVICES clock-web"; fi + if [ "${{ needs.detect-changes.outputs.contacts-backend }}" == "true" ]; then SERVICES="$SERVICES contacts-backend"; fi + if [ "${{ needs.detect-changes.outputs.contacts-web }}" == "true" ]; then SERVICES="$SERVICES contacts-web"; fi + if [ "${{ needs.detect-changes.outputs.matrix-mana-bot }}" == "true" ]; then SERVICES="$SERVICES matrix-mana-bot"; fi + fi + + echo "services=$SERVICES" >> $GITHUB_OUTPUT + echo "deploy-all=false" >> $GITHUB_OUTPUT + echo "Services to deploy: $SERVICES" + + - name: Deploy all services + if: steps.services.outputs.deploy-all == 'true' + run: | + cd "${{ env.PROJECT_DIR }}" + echo "=== Rebuilding and restarting ALL services ===" + docker compose -f "${{ env.COMPOSE_FILE }}" --env-file "${{ env.ENV_FILE }}" up -d --build + echo "=== Waiting for services to start ===" + sleep 15 + docker compose -f "${{ env.COMPOSE_FILE }}" ps + + - name: Deploy changed services + if: steps.services.outputs.deploy-all == 'false' && steps.services.outputs.services != '' + run: | + cd "${{ env.PROJECT_DIR }}" + SERVICES="${{ steps.services.outputs.services }}" + echo "=== Rebuilding: $SERVICES ===" + docker compose -f "${{ env.COMPOSE_FILE }}" --env-file "${{ env.ENV_FILE }}" up -d --build --no-deps $SERVICES + echo "=== Waiting for services to start ===" + sleep 10 + + - name: Health checks + run: | + cd "${{ env.PROJECT_DIR }}" + + check_health() { + local name=$1 + local url=$2 + local status=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$url" 2>/dev/null || echo "000") + if [ "$status" == "200" ]; then + echo " ✓ $name: OK" + else + echo " ✗ $name: FAILED (HTTP $status)" + fi + } + + echo "=== Health Checks ===" + check_health "Auth API" "http://localhost:3001/health" + check_health "Matrix Web" "http://localhost:5180/health" + check_health "Chat Backend" "http://localhost:3030/health" + check_health "Chat Web" "http://localhost:5010/health" + check_health "Todo Backend" "http://localhost:3031/health" + check_health "Todo Web" "http://localhost:5011/health" + check_health "Calendar Backend" "http://localhost:3032/health" + check_health "Calendar Web" "http://localhost:5012/health" + check_health "Clock Backend" "http://localhost:3033/health" + check_health "Clock Web" "http://localhost:5013/health" + + - name: Summary + run: | + echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Trigger:** ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY + echo "**Commit:** ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY + if [ "${{ steps.services.outputs.deploy-all }}" == "true" ]; then + echo "**Services:** All" >> $GITHUB_STEP_SUMMARY + else + echo "**Services:** ${{ steps.services.outputs.services }}" >> $GITHUB_STEP_SUMMARY + fi diff --git a/docs/MAC_MINI_RUNNER_SETUP.md b/docs/MAC_MINI_RUNNER_SETUP.md new file mode 100644 index 000000000..212ab7b8d --- /dev/null +++ b/docs/MAC_MINI_RUNNER_SETUP.md @@ -0,0 +1,119 @@ +# GitHub Actions Self-Hosted Runner auf dem Mac Mini einrichten + +Diese Anleitung auf dem Mac Mini ausführen. + +## 1. Runner-Token holen + +1. Gehe zu: https://github.com/Memo-2023/manacore-monorepo/settings/actions/runners/new +2. Wähle **macOS** und **ARM64** +3. Kopiere den Token aus dem `--token` Parameter (sieht so aus: `AXXXX...`) + +## 2. Runner installieren + +```bash +# Verzeichnis erstellen +mkdir -p ~/actions-runner && cd ~/actions-runner + +# Runner herunterladen (ARM64 macOS) +curl -o actions-runner.tar.gz -L https://github.com/actions/runner/releases/latest/download/actions-runner-osx-arm64-2.322.0.tar.gz + +# Entpacken +tar xzf actions-runner.tar.gz + +# Konfigurieren (Token von Schritt 1 einsetzen) +./config.sh --url https://github.com/Memo-2023/manacore-monorepo --token DEIN_TOKEN_HIER + +# Bei den Prompts: +# Runner group: [Enter] (default) +# Runner name: mac-mini +# Labels: [Enter] (default: self-hosted,macOS,ARM64) +# Work folder: [Enter] (default: _work) +``` + +## 3. Als LaunchAgent einrichten (Autostart) + +```bash +# Installiert den LaunchAgent automatisch +cd ~/actions-runner +./svc.sh install + +# Starten +./svc.sh start + +# Status prüfen +./svc.sh status +``` + +## 4. Prüfen ob es läuft + +```bash +# Lokal prüfen +./svc.sh status + +# Oder im Browser: +# https://github.com/Memo-2023/manacore-monorepo/settings/actions/runners +# → Runner "mac-mini" sollte als "Idle" angezeigt werden +``` + +## 5. Docker-Zugriff sicherstellen + +Der Runner braucht Zugriff auf Docker: + +```bash +# Prüfen ob Docker läuft +docker info + +# Prüfen ob docker compose funktioniert +docker compose version + +# Prüfen ob das Projekt-Verzeichnis existiert +ls ~/projects/manacore-monorepo/docker-compose.macmini.yml +``` + +## 6. Test-Deployment auslösen + +Entweder: +- Push auf `main` machen (deployt automatisch geänderte Services) +- Oder manuell: https://github.com/Memo-2023/manacore-monorepo/actions/workflows/cd-macmini.yml → "Run workflow" → Service wählen + +## Fehlerbehebung + +### Runner ist offline + +```bash +cd ~/actions-runner +./svc.sh status +./svc.sh stop +./svc.sh start +``` + +### Runner-Token abgelaufen + +```bash +cd ~/actions-runner +./svc.sh stop +./config.sh remove --token ALTES_TOKEN +# Neuen Token holen (siehe Schritt 1) +./config.sh --url https://github.com/Memo-2023/manacore-monorepo --token NEUES_TOKEN +./svc.sh start +``` + +### Logs ansehen + +```bash +# Runner-Logs +tail -f ~/actions-runner/_diag/Runner_*.log + +# Workflow-Logs: im GitHub UI unter Actions-Tab +``` + +## Was passiert nach dem Setup? + +Bei jedem Push auf `main`: +1. Der Runner erkennt welche Services sich geändert haben +2. Pullt den neuesten Code (`git pull`) +3. Baut nur die geänderten Docker-Container neu (`docker compose up -d --build `) +4. Führt Health Checks durch +5. Ergebnis ist im GitHub Actions-Tab sichtbar + +Manuelle Deploys sind jederzeit möglich über den "Run workflow" Button im Actions-Tab.