From 5a79646daf80311329b67a93b62f0d42a7e2b821 Mon Sep 17 00:00:00 2001 From: Michael Schiemer Date: Sat, 1 Nov 2025 19:02:09 +0100 Subject: [PATCH] chore: sync staging workspace --- Makefile | 8 +- deployment/DOCUMENTATION_INDEX.md | 169 ------- deployment/README.md | 85 ++-- deployment/ansible/README.md | 56 ++- deployment/ansible/ansible.cfg | 1 + deployment/ansible/group_vars/production.yml | 1 + .../ansible/playbooks/check-staging-logs.yml | 66 +++ .../ansible/playbooks/deploy-update.yml | 41 +- .../playbooks/diagnose-staging-502.yml | 96 ++++ .../playbooks/fix-staging-complete.yml | 71 +++ .../ansible/playbooks/fix-staging-issues.yml | 83 ++++ .../playbooks/setup-infrastructure.yml | 453 ++---------------- .../roles/application/defaults/main.yml | 34 ++ .../roles/application/tasks/deploy.yml | 69 +++ .../ansible/roles/application/tasks/main.yml | 7 + .../ansible/roles/application/tasks/sync.yml | 94 ++++ .../ansible/roles/gitea/defaults/main.yml | 4 + deployment/ansible/roles/gitea/tasks/main.yml | 35 ++ .../ansible/roles/minio/defaults/main.yml | 6 + deployment/ansible/roles/minio/tasks/main.yml | 90 ++++ .../roles/monitoring/defaults/main.yml | 5 + .../ansible/roles/monitoring/tasks/main.yml | 68 +++ .../roles/postgresql/defaults/main.yml | 4 + .../ansible/roles/postgresql/tasks/main.yml | 23 + .../ansible/roles/registry/defaults/main.yml | 5 + .../ansible/roles/registry/tasks/main.yml | 115 +++++ .../ansible/roles/traefik/defaults/main.yml | 4 + .../ansible/roles/traefik/tasks/main.yml | 23 + deployment/docs/README.md | 198 ++++++++ deployment/docs/RESTRUCTURE_PROPOSAL.md | 223 +++++++++ .../guides/code-change-workflow.md} | 4 +- .../guides/deployment-commands.md} | 6 +- .../guides/quick-start.md} | 2 +- .../guides/setup-guide.md} | 0 deployment/docs/guides/vault-password.md | 262 ++++++++++ .../history/cleanup-log.md} | 0 .../history/cleanup-summary.md} | 0 .../reference/application-stack.md} | 4 +- .../reference/workflow-troubleshooting.md} | 0 .../status/ci-cd-status.md} | 4 +- .../status/deployment-summary.md} | 0 .../status/deployment-todo.md} | 0 .../status/improvements.md} | 0 .../status/next-steps.md} | 0 .../tests/git-deployment-issue.md} | 0 .../tests/git-deployment-test.md} | 0 .../tests/quick-test.md} | 0 .../tests/recommended-test-flow.md} | 0 .../tests/test-git-deployment.md} | 0 .../tests/test-results.md} | 0 deployment/stacks/staging/docker-compose.yml | 6 + docs/Makefile.md | 66 +++ scripts/delete-all-workflow-runs.sh | 169 +++++++ scripts/extract-ci-token.yml | 11 + scripts/get-ci-token-from-vault.sh | 44 ++ src/Framework/Config/ConfigValidator.php | 5 +- src/Framework/Http/MiddlewareInvoker.php | 21 +- src/Framework/Logging/LoggerInitializer.php | 3 +- 58 files changed, 2035 insertions(+), 709 deletions(-) delete mode 100644 deployment/DOCUMENTATION_INDEX.md create mode 100644 deployment/ansible/playbooks/check-staging-logs.yml create mode 100644 deployment/ansible/playbooks/diagnose-staging-502.yml create mode 100644 deployment/ansible/playbooks/fix-staging-complete.yml create mode 100644 deployment/ansible/playbooks/fix-staging-issues.yml create mode 100644 deployment/ansible/roles/application/defaults/main.yml create mode 100644 deployment/ansible/roles/application/tasks/deploy.yml create mode 100644 deployment/ansible/roles/application/tasks/main.yml create mode 100644 deployment/ansible/roles/application/tasks/sync.yml create mode 100644 deployment/ansible/roles/gitea/defaults/main.yml create mode 100644 deployment/ansible/roles/gitea/tasks/main.yml create mode 100644 deployment/ansible/roles/minio/defaults/main.yml create mode 100644 deployment/ansible/roles/minio/tasks/main.yml create mode 100644 deployment/ansible/roles/monitoring/defaults/main.yml create mode 100644 deployment/ansible/roles/monitoring/tasks/main.yml create mode 100644 deployment/ansible/roles/postgresql/defaults/main.yml create mode 100644 deployment/ansible/roles/postgresql/tasks/main.yml create mode 100644 deployment/ansible/roles/registry/defaults/main.yml create mode 100644 deployment/ansible/roles/registry/tasks/main.yml create mode 100644 deployment/ansible/roles/traefik/defaults/main.yml create mode 100644 deployment/ansible/roles/traefik/tasks/main.yml create mode 100644 deployment/docs/README.md create mode 100644 deployment/docs/RESTRUCTURE_PROPOSAL.md rename deployment/{CODE_CHANGE_WORKFLOW.md => docs/guides/code-change-workflow.md} (98%) rename deployment/{DEPLOYMENT_COMMANDS.md => docs/guides/deployment-commands.md} (94%) rename deployment/{QUICK_START.md => docs/guides/quick-start.md} (98%) rename deployment/{SETUP-GUIDE.md => docs/guides/setup-guide.md} (100%) create mode 100644 deployment/docs/guides/vault-password.md rename deployment/{CLEANUP_LOG.md => docs/history/cleanup-log.md} (100%) rename deployment/{CLEANUP_SUMMARY.md => docs/history/cleanup-summary.md} (100%) rename deployment/{APPLICATION_STACK_DEPLOYMENT.md => docs/reference/application-stack.md} (98%) rename deployment/{WORKFLOW-TROUBLESHOOTING.md => docs/reference/workflow-troubleshooting.md} (100%) rename deployment/{CI_CD_STATUS.md => docs/status/ci-cd-status.md} (96%) rename deployment/{DEPLOYMENT_SUMMARY.md => docs/status/deployment-summary.md} (100%) rename deployment/{DEPLOYMENT-TODO.md => docs/status/deployment-todo.md} (100%) rename deployment/{IMPROVEMENTS.md => docs/status/improvements.md} (100%) rename deployment/{NEXT_STEPS.md => docs/status/next-steps.md} (100%) rename deployment/{GIT_DEPLOYMENT_ISSUE.md => docs/tests/git-deployment-issue.md} (100%) rename deployment/{GIT_DEPLOYMENT_TEST.md => docs/tests/git-deployment-test.md} (100%) rename deployment/{QUICK_TEST.md => docs/tests/quick-test.md} (100%) rename deployment/{RECOMMENDED_TEST_FLOW.md => docs/tests/recommended-test-flow.md} (100%) rename deployment/{TEST_GIT_DEPLOYMENT.md => docs/tests/test-git-deployment.md} (100%) rename deployment/{TEST_RESULT.md => docs/tests/test-results.md} (100%) create mode 100644 docs/Makefile.md create mode 100755 scripts/delete-all-workflow-runs.sh create mode 100644 scripts/extract-ci-token.yml create mode 100755 scripts/get-ci-token-from-vault.sh diff --git a/Makefile b/Makefile index f4f22337..51696353 100644 --- a/Makefile +++ b/Makefile @@ -187,7 +187,6 @@ setup-server: ## Server-Grundkonfiguration check: ## Serververbindung prüfen ansible -i $(ANSIBLE_INVENTORY) all -m ping $(if $(LIMIT),--limit="$(LIMIT)",) - # Beispielaufrufe: # make staging TAGS="deploy,check" # make setup-server LIMIT="staging" TAGS="docker" @@ -198,7 +197,7 @@ update-production: ## Update PHP files on production server @cd deployment && make application ENV=production @echo "✅ Production update completed" -restart-production: ## Restart production PHP container only +restart-production: ## Restart production PHP container only @echo "🔄 Restarting production PHP container..." @ssh -i ~/.ssh/production deploy@94.16.110.151 "cd /var/www/html && docker compose restart php" @echo "✅ Production PHP container restarted" @@ -247,4 +246,7 @@ ssl-backup: ## Backup Let's Encrypt certificates alpine tar czf /backup/letsencrypt-$(shell date +%Y%m%d-%H%M%S).tar.gz /etc/letsencrypt @echo "✅ Backup created in backups/" -.PHONY: up down build restart logs ps phpinfo deploy setup clean clean-coverage status fix-ssh-perms setup-ssh test test-coverage test-coverage-html test-unit test-framework test-domain test-watch test-parallel test-profile test-filter security-check security-audit-json security-check-prod update-production restart-production deploy-production-quick status-production logs-production ssl-init ssl-init-staging ssl-test ssl-renew ssl-status ssl-backup +push-staging: ## Pusht den aktuellen Stand nach origin/staging + git push origin HEAD:staging + +.PHONY: up down build restart logs ps phpinfo deploy setup clean clean-coverage status fix-ssh-perms setup-ssh test test-coverage test-coverage-html test-unit test-framework test-domain test-watch test-parallel test-profile test-filter security-check security-audit-json security-check-prod update-production restart-production deploy-production-quick status-production logs-production ssl-init ssl-init-staging ssl-test ssl-renew ssl-status ssl-backup push-staging diff --git a/deployment/DOCUMENTATION_INDEX.md b/deployment/DOCUMENTATION_INDEX.md deleted file mode 100644 index c5e8fa4b..00000000 --- a/deployment/DOCUMENTATION_INDEX.md +++ /dev/null @@ -1,169 +0,0 @@ -# Deployment Dokumentation - Index - -**Stand:** 2025-10-31 -**Status:** ✅ Vollständige Dokumentation vorhanden - ---- - -## 🚀 Schnellstart - -### Für schnellen Einstieg - -1. **[QUICK_START.md](QUICK_START.md)** ⭐ - - Schnellstart-Guide - - Pipeline-Status prüfen - - Troubleshooting Quick Reference - -2. **[CODE_CHANGE_WORKFLOW.md](CODE_CHANGE_WORKFLOW.md)** - - Wie Codeänderungen gepusht werden - - Automatisches vs. manuelles Deployment - - Branching-Strategien - - Beispiel-Workflows - ---- - -## 📚 Detaillierte Guides - -### Deployment-Prozess - -3. **[APPLICATION_STACK_DEPLOYMENT.md](APPLICATION_STACK_DEPLOYMENT.md)** - - Detaillierter Deployment-Ablauf Schritt für Schritt - - Was passiert bei jedem Deployment - - Container-Neustart Details - - Rollback-Prozess - - Troubleshooting - -### CI/CD Pipeline - -4. **[CI_CD_STATUS.md](CI_CD_STATUS.md)** - - Aktueller CI/CD Status - - Secrets-Übersicht - - Runner-Status - - Checkliste für Completion - - Troubleshooting - -### Setup & Konfiguration - -5. **[SETUP-GUIDE.md](SETUP-GUIDE.md)** - - Kompletter Setup-Guide von Anfang bis Ende - - Infrastructure Deployment - - Gitea Runner Setup - - Secrets Konfiguration - - Schritt-für-Schritt Anleitung - ---- - -## 📊 Status & Übersicht - -### Projekt-Status - -6. **[DEPLOYMENT_SUMMARY.md](DEPLOYMENT_SUMMARY.md)** - - Was ist fertig? - - Was fehlt noch? - - Completion Rate - - Nächste Schritte - -7. **[DEPLOYMENT-TODO.md](DEPLOYMENT_TODO.md)** - - Aktuelle TODO-Liste - - Priorisierte Reihenfolge - - Quick Checklist - -8. **[DEPLOYMENT-STATUS.md](DEPLOYMENT-STATUS.md)** - - Detaillierter Status aller Phasen - - Phasen-basierte Übersicht - - Historischer Status - ---- - -## 🔧 Stack-spezifische Dokumentation - -### Infrastructure Stacks - -- **[stacks/traefik/README.md](stacks/traefik/README.md)** - Reverse Proxy & SSL -- **[stacks/postgresql/README.md](stacks/postgresql/README.md)** - Database mit Backups -- **[stacks/registry/README.md](stacks/registry/README.md)** - Private Docker Registry -- **[stacks/gitea/README.md](stacks/gitea/README.md)** - Git Server & CI/CD -- **[stacks/monitoring/README.md](stacks/monitoring/README.md)** - Monitoring Tools - -### Application Stack - -- **[stacks/application/README.md](stacks/application/README.md)** - Application Stack Details -- **[ansible/README.md](ansible/README.md)** - Ansible Playbooks Dokumentation - -### CI/CD - -- **[gitea-runner/README.md](gitea-runner/README.md)** - Gitea Runner Setup -- **[.gitea/workflows/production-deploy.yml](../.gitea/workflows/production-deploy.yml)** - Haupt-Deployment-Pipeline - ---- - -## 🔐 Security & VPN - -- **[docs/WIREGUARD-SETUP.md](docs/WIREGUARD-SETUP.md)** - WireGuard VPN Setup -- **[ansible/playbooks/README-WIREGUARD.md](ansible/playbooks/README-WIREGUARD.md)** - WireGuard Ansible Playbooks - ---- - -## 🆘 Troubleshooting - -### Workflow-Probleme - -- **[WORKFLOW-TROUBLESHOOTING.md](WORKFLOW-TROUBLESHOOTING.md)** - Workflow Troubleshooting -- **[NATIVE-WORKFLOW-README.md](NATIVE-WORKFLOW-README.md)** - Native Workflow ohne GitHub Actions - -### Allgemeine Hilfe - -- **[QUICK_START.md](QUICK_START.md)** - Troubleshooting Quick Reference -- **[CI_CD_STATUS.md](CI_CD_STATUS.md)** - CI/CD Troubleshooting -- **[APPLICATION_STACK_DEPLOYMENT.md](APPLICATION_STACK_DEPLOYMENT.md)** - Deployment Troubleshooting - ---- - -## 📖 Haupt-Dokumentation - -- **[README.md](README.md)** - Haupt-Dokumentation & Übersicht -- **[SETUP-GUIDE.md](SETUP-GUIDE.md)** - Kompletter Setup-Guide - ---- - -## 🎯 Empfohlene Lesereihenfolge - -### Für neue Nutzer - -1. **[QUICK_START.md](QUICK_START.md)** - Schneller Überblick -2. **[CODE_CHANGE_WORKFLOW.md](CODE_CHANGE_WORKFLOW.md)** - Code deployen lernen -3. **[DEPLOYMENT_SUMMARY.md](DEPLOYMENT_SUMMARY.md)** - Projekt-Status verstehen - -### Für Deployment-Verständnis - -1. **[APPLICATION_STACK_DEPLOYMENT.md](APPLICATION_STACK_DEPLOYMENT.md)** - Wie Deployment funktioniert -2. **[CI_CD_STATUS.md](CI_CD_STATUS.md)** - CI/CD Pipeline verstehen -3. **[SETUP-GUIDE.md](SETUP-GUIDE.md)** - Komplette Setup-Anleitung - -### Für Troubleshooting - -1. **[QUICK_START.md](QUICK_START.md)** - Quick Troubleshooting -2. **[WORKFLOW-TROUBLESHOOTING.md](WORKFLOW-TROUBLESHOOTING.md)** - Workflow-Probleme -3. Stack-spezifische READMEs für Details - ---- - -## 📝 Dokumentations-Standards - -**Alle Dokumentationsdateien:** -- Verwenden Markdown-Format -- Haben klare Überschriften-Struktur -- Enthalten Code-Beispiele -- Haben Troubleshooting-Abschnitte (wenn relevant) -- Verlinken zu verwandten Dokumenten - -**Standards:** -- ✅ Beispiele sind ausführbar -- ✅ Pfade sind absolut oder relativ klar -- ✅ Screenshots/Links sind aktuell -- ✅ Status ist klar markiert (✅/⚠️/❌) - ---- - -**Letzte Aktualisierung:** 2025-10-31 -**Status:** ✅ Dokumentation vollständig diff --git a/deployment/README.md b/deployment/README.md index aa9f87de..a0ccc156 100644 --- a/deployment/README.md +++ b/deployment/README.md @@ -43,18 +43,24 @@ Developer → git push ``` deployment/ +├── ansible/ # Ansible config, playbooks, inventory, templates +├── gitea-runner/ # Self-hosted Gitea Actions runner stack ├── stacks/ # Docker Compose stacks -│ ├── traefik/ # Reverse proxy with SSL -│ ├── gitea/ # Git server -│ ├── registry/ # Private Docker registry │ ├── application/ # Main PHP application -│ ├── postgres/ # Database -│ └── monitoring/ # Portainer + Grafana + Prometheus -├── ansible/ # Automation playbooks -│ ├── playbooks/ # Deployment automation -│ ├── inventory/ # Server inventory -│ └── secrets/ # Ansible Vault secrets -└── docs/ # Deployment documentation +│ ├── gitea/ # Git server +│ ├── minio/ # Object storage +│ ├── monitoring/ # Portainer, Grafana, Prometheus +│ ├── postgresql/ # PostgreSQL database +│ ├── registry/ # Private Docker registry +│ ├── staging/ # Optional staging stack +│ └── traefik/ # Reverse proxy with SSL certificates +├── docs/ # 📚 Dokumentation (siehe docs/README.md) +│ ├── guides/ # Anleitungen & Guides +│ ├── reference/ # Referenz-Dokumentation +│ ├── status/ # Status & Tracking +│ ├── tests/ # Test-Dokumentation +│ └── history/ # Logs & Historie +└── README.md (dieses Dokument) ``` ## Getting Started @@ -70,7 +76,7 @@ git push origin main # → Automatisches Deployment! **Pipeline-Status:** `https://git.michaelschiemer.de/michael/michaelschiemer/actions` -**📖 Vollständige Anleitung:** Siehe [QUICK_START.md](QUICK_START.md) oder [CODE_CHANGE_WORKFLOW.md](CODE_CHANGE_WORKFLOW.md) +**📖 Vollständige Anleitung:** Siehe [docs/guides/quick-start.md](docs/guides/quick-start.md) oder [docs/guides/code-change-workflow.md](docs/guides/code-change-workflow.md) --- @@ -113,7 +119,7 @@ Each stack has its own README with detailed configuration: - [Gitea](stacks/gitea/README.md) - Git server configuration - [Registry](stacks/registry/README.md) - Private registry setup - [Application](stacks/application/README.md) - Application deployment -- [PostgreSQL](stacks/postgres/README.md) - Database configuration +- [PostgreSQL](stacks/postgresql/README.md) - Database configuration - [Monitoring](stacks/monitoring/README.md) - Monitoring stack ## Deployment Commands @@ -141,7 +147,7 @@ ansible-playbook -i inventory/production.yml \ playbooks/rollback.yml ``` -**📖 Vollständige Command-Referenz:** Siehe [DEPLOYMENT_COMMANDS.md](DEPLOYMENT_COMMANDS.md) +**📖 Vollständige Command-Referenz:** Siehe [docs/guides/deployment-commands.md](docs/guides/deployment-commands.md) ### Update Specific Stack ```bash @@ -183,11 +189,11 @@ git push origin main **📖 Vollständige Dokumentation:** -- **[QUICK_START.md](QUICK_START.md)** ⭐ - Schnellstart-Guide für Deployment -- **[CODE_CHANGE_WORKFLOW.md](CODE_CHANGE_WORKFLOW.md)** - Kompletter Guide für Codeänderungen -- **[APPLICATION_STACK_DEPLOYMENT.md](APPLICATION_STACK_DEPLOYMENT.md)** - Detaillierter Deployment-Ablauf -- **[CI_CD_STATUS.md](CI_CD_STATUS.md)** - CI/CD Pipeline Status & Checkliste -- **[DEPLOYMENT_SUMMARY.md](DEPLOYMENT_SUMMARY.md)** - Projekt-Status Übersicht +- **[docs/guides/quick-start.md](docs/guides/quick-start.md)** ⭐ - Schnellstart-Guide für Deployment +- **[docs/guides/code-change-workflow.md](docs/guides/code-change-workflow.md)** - Kompletter Guide für Codeänderungen +- **[docs/reference/application-stack.md](docs/reference/application-stack.md)** - Detaillierter Deployment-Ablauf +- **[docs/status/ci-cd-status.md](docs/status/ci-cd-status.md)** - CI/CD Pipeline Status & Checkliste +- **[docs/status/deployment-summary.md](docs/status/deployment-summary.md)** - Projekt-Status Übersicht ### Pipeline Details @@ -207,21 +213,13 @@ Access monitoring tools: ## Backup & Recovery -### Automated Backups +### Current State -- **PostgreSQL**: Daily backups with 7-day retention -- **Gitea Data**: Weekly backups -- **Registry Images**: On-demand backups +Infrastructure backups are handled per stack. The PostgreSQL stack ships helper scripts under `stacks/postgresql/scripts/` (see `backup.sh` and `restore.sh`). Registry and Gitea data snapshots are currently managed manually on the host. -### Manual Backup -```bash -ansible-playbook -i inventory/production.yml playbooks/backup.yml -``` +### Roadmap -### Restore from Backup -```bash -ansible-playbook -i inventory/production.yml playbooks/restore.yml -``` +An Ansible-level backup/restore playbook is still planned. Track progress in `DEPLOYMENT-TODO.md` and update this section once the playbook is available. ## Security @@ -248,7 +246,7 @@ docker network inspect traefik-public ### View Logs ```bash # Application logs -docker compose -f stacks/application/docker-compose.yml logs -f app-php +docker compose -f stacks/application/docker-compose.yml logs -f app # Traefik logs docker compose -f stacks/traefik/docker-compose.yml logs -f @@ -256,25 +254,22 @@ docker compose -f stacks/traefik/docker-compose.yml logs -f ## 📚 Dokumentation Index -**Vollständige Dokumentations-Übersicht:** Siehe [DOCUMENTATION_INDEX.md](DOCUMENTATION_INDEX.md) +**Vollständige Dokumentations-Übersicht:** Siehe [docs/README.md](docs/README.md) **Wichtigste Dokumente:** -- **[QUICK_START.md](QUICK_START.md)** ⭐ - Schnellstart -- **[CODE_CHANGE_WORKFLOW.md](CODE_CHANGE_WORKFLOW.md)** - Code deployen -- **[APPLICATION_STACK_DEPLOYMENT.md](APPLICATION_STACK_DEPLOYMENT.md)** - Deployment-Details -- **[CI_CD_STATUS.md](CI_CD_STATUS.md)** - CI/CD Status -- **[DEPLOYMENT_SUMMARY.md](DEPLOYMENT_SUMMARY.md)** - Projekt-Übersicht +- **[docs/guides/quick-start.md](docs/guides/quick-start.md)** ⭐ - Schnellstart +- **[docs/guides/code-change-workflow.md](docs/guides/code-change-workflow.md)** - Code deployen +- **[docs/reference/application-stack.md](docs/reference/application-stack.md)** - Deployment-Details +- **[docs/status/ci-cd-status.md](docs/status/ci-cd-status.md)** - CI/CD Status +- **[docs/status/deployment-summary.md](docs/status/deployment-summary.md)** - Projekt-Übersicht ## Support -For issues and questions, see: -- [Troubleshooting Guide](docs/troubleshooting.md) -- [Workflow Troubleshooting](WORKFLOW-TROUBLESHOOTING.md) -- [CI/CD Status](CI_CD_STATUS.md) - Mit Troubleshooting-Section - -## Migration from Docker Swarm - -See [Migration Guide](docs/migration-from-swarm.md) for detailed instructions on migrating from the old Docker Swarm setup. +Für spezifische Fragen helfen die folgenden Dokumente weiter: +- [docs/reference/workflow-troubleshooting.md](docs/reference/workflow-troubleshooting.md) – Fehleranalyse für Laufzeiten & Pipelines +- [docs/status/ci-cd-status.md](docs/status/ci-cd-status.md) – Pipeline-Status & Checklisten +- [docs/status/deployment-summary.md](docs/status/deployment-summary.md) – Aktueller Projektüberblick +- [docs/reference/application-stack.md](docs/reference/application-stack.md) – Detaillierte Deployment-Schritte ## License diff --git a/deployment/ansible/README.md b/deployment/ansible/README.md index d78a47e7..70154221 100644 --- a/deployment/ansible/README.md +++ b/deployment/ansible/README.md @@ -6,21 +6,35 @@ This directory contains Ansible playbooks and configuration for deploying the Cu ``` deployment/ansible/ -├── ansible.cfg # Ansible configuration +├── ansible.cfg # Ansible configuration ├── inventory/ -│ └── production.yml # Production server inventory +│ ├── production.yml # Production server inventory +│ └── local.yml # Local testing inventory ├── playbooks/ -│ ├── setup-production-secrets.yml # Deploy secrets │ ├── deploy-update.yml # Deploy application updates │ ├── rollback.yml # Rollback deployments +│ ├── setup-infrastructure.yml # Provision core stacks +│ ├── setup-production-secrets.yml # Deploy secrets │ ├── setup-wireguard.yml # Setup WireGuard VPN server │ ├── add-wireguard-client.yml # Add WireGuard client +│ ├── sync-code.yml # Git-based code sync │ └── README-WIREGUARD.md # WireGuard documentation +├── scripts/ # Helper scripts for secrets & credentials +├── roles/ # Reusable roles (e.g. application stack) ├── secrets/ │ ├── .gitignore # Prevent committing secrets │ └── production.vault.yml.example # Example vault file └── templates/ - └── .env.production.j2 # Environment file template + ├── application.env.j2 # Application stack environment + ├── gitea-app.ini.j2 # Gitea configuration + ├── minio.env.j2 # MinIO environment + ├── monitoring.env.j2 # Monitoring stack environment + ├── wireguard-client.conf.j2 # WireGuard client config + └── wireguard-server.conf.j2 # WireGuard server config + +## Roles + +Stack-spezifische Aufgaben liegen in `roles/` (z. B. `application`, `traefik`, `registry`). Playbooks wie `setup-infrastructure.yml` importieren diese Rollen direkt. Die Application-Rolle kann mit Variablen wie `application_sync_files=false` oder `application_compose_recreate="always"` konfiguriert werden (siehe `playbooks/deploy-update.yml` als Beispiel). ## Prerequisites @@ -37,6 +51,7 @@ deployment/ansible/ - Create `.vault_pass` file in `secrets/` directory - Add vault password to this file (one line) - File is gitignored for security + - 📖 **Detaillierte Dokumentation:** [docs/guides/vault-password.md](../docs/guides/vault-password.md) ## Setup Instructions @@ -66,6 +81,8 @@ echo "your-vault-password-here" > secrets/.vault_pass chmod 600 secrets/.vault_pass ``` +**📖 Für detaillierte Informationen:** Siehe [docs/guides/vault-password.md](../docs/guides/vault-password.md) + ### 3. Configure SSH Key ```bash @@ -265,11 +282,12 @@ docker login registry.michaelschiemer.de # SSH to production server ssh -i ~/.ssh/production deploy@94.16.110.151 -# Check Docker service logs -docker service logs app_app +# Check Docker Compose service logs +docker compose -f {{ app_stack_path }}/docker-compose.yml logs app +docker compose -f {{ app_stack_path }}/docker-compose.yml logs nginx # Check stack status -docker stack ps app +docker compose -f {{ app_stack_path }}/docker-compose.yml ps ``` ## CI/CD Integration @@ -283,19 +301,19 @@ Vault password is stored as Gitea Actions secret: `ANSIBLE_VAULT_PASSWORD` ## Inventory Variables -All deployment variables are defined in `inventory/production.yml`: +All zentralen Variablen werden in `group_vars/production.yml` gepflegt und können bei Bedarf im Inventory überschrieben werden. Häufig verwendete Werte: -| Variable | Description | Default | -|----------|-------------|---------| -| `docker_registry` | Docker registry URL | registry.michaelschiemer.de | -| `app_name` | Application name | framework | -| `app_domain` | Production domain | michaelschiemer.de | -| `stack_name` | Docker stack name | app | -| `compose_file` | Docker Compose file path | /home/deploy/docker-compose.prod.yml | -| `secrets_path` | Secrets directory | /home/deploy/secrets | -| `backups_path` | Backups directory | /home/deploy/backups | -| `max_rollback_versions` | Backup retention | 5 | -| `health_check_url` | Health check endpoint | https://michaelschiemer.de/health | +| Variable | Beschreibung | Standardwert | +|----------|--------------|--------------| +| `deploy_user_home` | Home-Verzeichnis des Deploy-Users | `/home/deploy` | +| `stacks_base_path` | Basispfad für Docker Compose Stacks | `/home/deploy/deployment/stacks` | +| `app_stack_path` | Pfad zum Application Stack | `/home/deploy/deployment/stacks/application` | +| `backups_path` | Ablageort für Deployment-Backups | `/home/deploy/deployment/backups` | +| `docker_registry` | Interner Registry-Endpunkt (lokal) | `localhost:5000` | +| `docker_registry_external` | Externer Registry-Endpunkt | `registry.michaelschiemer.de` | +| `app_domain` | Produktions-Domain | `michaelschiemer.de` | +| `health_check_url` | Health-Check Endpoint | `https://michaelschiemer.de/health` | +| `max_rollback_versions` | Anzahl vorgehaltener Backups | `5` | ## Backup Management diff --git a/deployment/ansible/ansible.cfg b/deployment/ansible/ansible.cfg index aedebd22..50001550 100644 --- a/deployment/ansible/ansible.cfg +++ b/deployment/ansible/ansible.cfg @@ -9,6 +9,7 @@ gathering = smart fact_caching = jsonfile fact_caching_connection = /tmp/ansible_facts fact_caching_timeout = 3600 +roles_path = roles [ssh_connection] pipelining = True diff --git a/deployment/ansible/group_vars/production.yml b/deployment/ansible/group_vars/production.yml index a2fd9f8b..199ba9f3 100644 --- a/deployment/ansible/group_vars/production.yml +++ b/deployment/ansible/group_vars/production.yml @@ -15,6 +15,7 @@ docker_registry_external: "registry.michaelschiemer.de" docker_registry_username_default: "admin" # docker_registry_password_default should be set in vault as vault_docker_registry_password # If not using vault, override via -e docker_registry_password_default="your-password" +docker_registry_password_default: "" registry_auth_path: "{{ stacks_base_path }}/registry/auth" # Application Configuration diff --git a/deployment/ansible/playbooks/check-staging-logs.yml b/deployment/ansible/playbooks/check-staging-logs.yml new file mode 100644 index 00000000..41b17361 --- /dev/null +++ b/deployment/ansible/playbooks/check-staging-logs.yml @@ -0,0 +1,66 @@ +--- +- name: Check Staging Container Logs + hosts: production + gather_facts: yes + become: no + + tasks: + - name: Check staging container status + shell: | + cd ~/deployment/stacks/staging && docker compose ps + args: + executable: /bin/bash + register: container_status + changed_when: false + + - name: Get staging-app logs + shell: | + cd ~/deployment/stacks/staging && docker compose logs --tail=100 staging-app + args: + executable: /bin/bash + register: staging_app_logs + changed_when: false + + - name: Get staging-nginx logs + shell: | + cd ~/deployment/stacks/staging && docker compose logs --tail=100 staging-nginx + args: + executable: /bin/bash + register: staging_nginx_logs + changed_when: false + + - name: Get staging-queue-worker logs + shell: | + cd ~/deployment/stacks/staging && docker compose logs --tail=100 staging-queue-worker + args: + executable: /bin/bash + register: staging_queue_logs + changed_when: false + + - name: Get staging-scheduler logs + shell: | + cd ~/deployment/stacks/staging && docker compose logs --tail=100 staging-scheduler + args: + executable: /bin/bash + register: staging_scheduler_logs + changed_when: false + + - name: Display container status + debug: + msg: "{{ container_status.stdout_lines }}" + + - name: Display staging-app logs + debug: + msg: "{{ staging_app_logs.stdout_lines }}" + + - name: Display staging-nginx logs + debug: + msg: "{{ staging_nginx_logs.stdout_lines }}" + + - name: Display staging-queue-worker logs + debug: + msg: "{{ staging_queue_logs.stdout_lines }}" + + - name: Display staging-scheduler logs + debug: + msg: "{{ staging_scheduler_logs.stdout_lines }}" diff --git a/deployment/ansible/playbooks/deploy-update.yml b/deployment/ansible/playbooks/deploy-update.yml index 14cf72c2..d69a1968 100644 --- a/deployment/ansible/playbooks/deploy-update.yml +++ b/deployment/ansible/playbooks/deploy-update.yml @@ -133,31 +133,13 @@ when: true register: compose_updated - - name: Restart application stack with new image - community.docker.docker_compose_v2: - project_src: "{{ app_stack_path }}" - state: present - pull: always - recreate: always - remove_orphans: yes - register: stack_deploy - # Always restart when deploying, even if image already exists - # This ensures code changes are applied even with same image tag - when: true - - - name: Wait for services to be healthy - wait_for: - timeout: 60 - changed_when: false - - - name: Check container health status - shell: | - docker compose -f {{ app_stack_path }}/docker-compose.yml ps --format json | jq -r '.[] | select(.Health != "healthy" and .Health != "") | "\(.Name): \(.Health)"' || echo "All healthy or no health checks" - args: - executable: /bin/bash - register: health_status - changed_when: false - ignore_errors: yes + - name: Redeploy application stack with new image + import_role: + name: application + vars: + application_sync_files: false + application_compose_recreate: "always" + application_remove_orphans: true - name: Get deployed image information shell: | @@ -175,8 +157,9 @@ Image Tag: {{ image_tag }} Deployed Image: {{ deployed_image.stdout }} Image Pull: {{ 'SUCCESS' if image_pull.changed else 'SKIPPED (already exists)' }} - Stack Deploy: {{ 'UPDATED' if stack_deploy.changed else 'NO_CHANGE' }} - Health Status: {{ health_status.stdout if health_status.stdout != '' else 'All services healthy' }} + Stack Deploy: {{ 'UPDATED' if application_stack_changed else 'NO_CHANGE' }} + Health Status: {{ application_health_output if application_health_output != '' else 'All services healthy' }} + Health Check HTTP Status: {{ application_healthcheck_status }} dest: "{{ backups_path }}/{{ deployment_timestamp | regex_replace(':', '-') }}/deployment_metadata.txt" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" @@ -200,7 +183,9 @@ - "Commit: {{ git_commit_sha }}" - "Timestamp: {{ deployment_timestamp }}" - "Image Pull: {{ 'SUCCESS' if image_pull.changed else 'SKIPPED' }}" - - "Stack Deploy: {{ 'UPDATED' if stack_deploy.changed else 'NO_CHANGE' }}" + - "Stack Deploy: {{ 'UPDATED' if application_stack_changed else 'NO_CHANGE' }}" + - "Health Output: {{ application_health_output if application_health_output != '' else 'All services healthy' }}" + - "Health Check HTTP Status: {{ application_healthcheck_status }}" - "Health Check URL: {{ health_check_url }}" - "" - "Next: Verify application is healthy" diff --git a/deployment/ansible/playbooks/diagnose-staging-502.yml b/deployment/ansible/playbooks/diagnose-staging-502.yml new file mode 100644 index 00000000..dafe1309 --- /dev/null +++ b/deployment/ansible/playbooks/diagnose-staging-502.yml @@ -0,0 +1,96 @@ +--- +- name: Diagnose Staging 502 Error + hosts: production + gather_facts: yes + become: no + + tasks: + - name: Check Nginx error logs + shell: | + cd ~/deployment/stacks/staging && docker compose logs --tail=50 staging-nginx 2>&1 | grep -iE "(error|502|bad gateway|php|fpm)" || echo "No errors found in nginx logs" + args: + executable: /bin/bash + register: nginx_errors + changed_when: false + + - name: Check PHP-FPM status in staging-app + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-app php-fpm-healthcheck 2>&1 || echo "PHP-FPM healthcheck failed" + args: + executable: /bin/bash + register: php_fpm_status + changed_when: false + ignore_errors: yes + + - name: Test PHP-FPM connection from nginx container + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "nc -zv staging-app 9000 2>&1 || echo 'Connection test failed'" || echo "Connection test failed" + args: + executable: /bin/bash + register: connection_test + changed_when: false + ignore_errors: yes + + - name: Check if PHP-FPM is listening + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-app sh -c "netstat -tlnp 2>/dev/null | grep 9000 || ss -tlnp 2>/dev/null | grep 9000 || echo 'Port 9000 not found'" || echo "Could not check ports" + args: + executable: /bin/bash + register: php_fpm_port + changed_when: false + ignore_errors: yes + + - name: Check staging-app process list + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-app sh -c "ps aux | grep -E '(php-fpm|nginx)' | head -20" || echo "Could not get process list" + args: + executable: /bin/bash + register: processes + changed_when: false + ignore_errors: yes + + - name: Check network connectivity + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "ping -c 2 staging-app 2>&1 || echo 'Ping failed'" || echo "Network test failed" + args: + executable: /bin/bash + register: network_test + changed_when: false + ignore_errors: yes + + - name: Check if /var/www/html/public/index.php exists + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "test -f /var/www/html/public/index.php && echo 'index.php exists' || echo 'index.php NOT FOUND'" || echo "Could not check file" + args: + executable: /bin/bash + register: index_php_check + changed_when: false + ignore_errors: yes + + - name: Display Nginx error logs + debug: + msg: "{{ nginx_errors.stdout_lines }}" + + - name: Display PHP-FPM status + debug: + msg: "{{ php_fpm_status.stdout_lines }}" + + - name: Display connection test + debug: + msg: "{{ connection_test.stdout_lines }}" + + - name: Display PHP-FPM port check + debug: + msg: "{{ php_fpm_port.stdout_lines }}" + + - name: Display processes + debug: + msg: "{{ processes.stdout_lines }}" + + - name: Display network test + debug: + msg: "{{ network_test.stdout_lines }}" + + - name: Display index.php check + debug: + msg: "{{ index_php_check.stdout_lines }}" diff --git a/deployment/ansible/playbooks/fix-staging-complete.yml b/deployment/ansible/playbooks/fix-staging-complete.yml new file mode 100644 index 00000000..5e57c607 --- /dev/null +++ b/deployment/ansible/playbooks/fix-staging-complete.yml @@ -0,0 +1,71 @@ +--- +- name: Complete Fix for Staging (502 + Git) + hosts: production + gather_facts: yes + become: no + + tasks: + - name: Check if index.php exists in staging-app + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-app sh -c "ls -la /var/www/html/public/index.php 2>&1" || echo "index.php NOT FOUND" + args: + executable: /bin/bash + register: index_php_app + changed_when: false + ignore_errors: yes + + - name: Check if index.php exists in staging-nginx + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "ls -la /var/www/html/public/index.php 2>&1" || echo "index.php NOT FOUND" + args: + executable: /bin/bash + register: index_php_nginx + changed_when: false + ignore_errors: yes + + - name: Check PHP-FPM listen configuration + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-app sh -c "cat /usr/local/etc/php-fpm.d/www.conf | grep -E '(^listen|^listen.allowed_clients|^listen.owner|^listen.group|^user|^group)' | head -20" || echo "Could not read config" + args: + executable: /bin/bash + register: php_fpm_full_config + changed_when: false + ignore_errors: yes + + - name: Test actual HTTP request to staging-app + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "curl -v http://staging-app:9000/index.php 2>&1 | head -30" || echo "HTTP test failed" + args: + executable: /bin/bash + register: http_test + changed_when: false + ignore_errors: yes + + - name: Fix Git ownership permanently in staging-app entrypoint + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-app sh -c "cd /var/www/html && git config --global --add safe.directory /var/www/html && git config --global --get-all safe.directory" || echo "Git config failed" + args: + executable: /bin/bash + register: git_config_check + changed_when: false + ignore_errors: yes + + - name: Display index.php check in app + debug: + msg: "{{ index_php_app.stdout_lines }}" + + - name: Display index.php check in nginx + debug: + msg: "{{ index_php_nginx.stdout_lines }}" + + - name: Display PHP-FPM config + debug: + msg: "{{ php_fpm_full_config.stdout_lines }}" + + - name: Display HTTP test + debug: + msg: "{{ http_test.stdout_lines }}" + + - name: Display git config + debug: + msg: "{{ git_config_check.stdout_lines }}" diff --git a/deployment/ansible/playbooks/fix-staging-issues.yml b/deployment/ansible/playbooks/fix-staging-issues.yml new file mode 100644 index 00000000..a29fbe2d --- /dev/null +++ b/deployment/ansible/playbooks/fix-staging-issues.yml @@ -0,0 +1,83 @@ +--- +- name: Fix Staging Issues (502 Error + Git Ownership) + hosts: production + gather_facts: yes + become: no + + tasks: + - name: Get recent nginx error logs + shell: | + cd ~/deployment/stacks/staging && docker compose logs --tail=100 staging-nginx 2>&1 + args: + executable: /bin/bash + register: nginx_all_logs + changed_when: false + + - name: Test PHP-FPM connection with curl + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "curl -v http://staging-app:9000 2>&1 | head -20" || echo "Connection test completed" + args: + executable: /bin/bash + register: curl_test + changed_when: false + ignore_errors: yes + + - name: Check PHP-FPM configuration + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-app sh -c "cat /usr/local/etc/php-fpm.d/www.conf | grep -E '(listen|listen.allowed_clients)' | head -10" || echo "Could not read PHP-FPM config" + args: + executable: /bin/bash + register: php_fpm_config + changed_when: false + ignore_errors: yes + + - name: Fix Git ownership issue in staging-app + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-app sh -c "cd /var/www/html && git config --global --add safe.directory /var/www/html && echo 'Git safe.directory configured'" || echo "Git config failed" + args: + executable: /bin/bash + register: git_fix + changed_when: false + ignore_errors: yes + + - name: Test if nginx can reach PHP-FPM + shell: | + cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "echo 'GET /index.php HTTP/1.0' | nc staging-app 9000 2>&1 | head -10" || docker compose exec -T staging-nginx sh -c "timeout 2 bash -c '&1 | grep -Ei "(configuration loaded|traefik[[:space:]]v|Starting provider|Server configuration loaded)" || true - args: - chdir: "{{ stacks_base_path }}/traefik" - register: traefik_logs - until: traefik_logs.stdout != "" - retries: 6 - delay: 10 - changed_when: false - ignore_errors: yes + import_role: + name: traefik # 2. Deploy PostgreSQL (Database) - name: Deploy PostgreSQL stack - community.docker.docker_compose_v2: - project_src: "{{ stacks_base_path }}/postgresql" - state: present - pull: always - register: postgres_output - - - name: Wait for PostgreSQL to be ready - wait_for: - timeout: "{{ wait_timeout }}" - when: postgres_output.changed - - - name: Check PostgreSQL logs for readiness - shell: docker compose logs postgres 2>&1 | grep -Ei "(ready to accept connections|database system is ready)" || true - args: - chdir: "{{ stacks_base_path }}/postgresql" - register: postgres_logs - until: postgres_logs.stdout != "" - retries: 6 - delay: 10 - changed_when: false - ignore_errors: yes + import_role: + name: postgresql # 3. Deploy Docker Registry (Private Registry) - - name: Ensure Registry auth directory exists - file: - path: "{{ registry_auth_path }}" - state: directory - mode: '0755' - become: yes - - - name: Optionally load registry credentials from vault - include_vars: - file: "{{ playbook_dir }}/../secrets/production.vault.yml" - no_log: yes - ignore_errors: yes - delegate_to: localhost - become: no - - - name: Set registry credentials from vault or defaults - set_fact: - registry_username: "{{ vault_docker_registry_username | default(docker_registry_username_default) }}" - registry_password: "{{ vault_docker_registry_password | default(docker_registry_password_default) }}" - no_log: true - - - name: Fail if registry password is not set - fail: - msg: "Registry password must be set in vault or docker_registry_password_default" - when: registry_password is not defined or registry_password == "" - - - name: Create Registry htpasswd file if missing - shell: | - if [ ! -f {{ registry_auth_path }}/htpasswd ]; then - docker run --rm --entrypoint htpasswd httpd:2 -Bbn {{ registry_username }} {{ registry_password }} > {{ registry_auth_path }}/htpasswd - chmod 644 {{ registry_auth_path }}/htpasswd - fi - args: - executable: /bin/bash - become: yes - changed_when: true - register: registry_auth_created - no_log: true - - name: Deploy Docker Registry stack - community.docker.docker_compose_v2: - project_src: "{{ stacks_base_path }}/registry" - state: present - pull: always - register: registry_output - - - name: Wait for Docker Registry to be ready - wait_for: - timeout: "{{ wait_timeout }}" - when: registry_output.changed - - - name: Check Registry logs for readiness - shell: docker compose logs registry 2>&1 | grep -Ei "(listening on|listening at|http server)" || true - args: - chdir: "{{ stacks_base_path }}/registry" - register: registry_logs - until: registry_logs.stdout != "" - retries: 6 - delay: 10 - changed_when: false - ignore_errors: yes - - - name: Verify Registry is accessible - uri: - url: "http://127.0.0.1:5000/v2/_catalog" - user: "{{ registry_username }}" - password: "{{ registry_password }}" - status_code: 200 - timeout: 5 - register: registry_check - ignore_errors: yes - changed_when: false - no_log: true - - - name: Display Registry status - debug: - msg: "Registry accessibility: {{ 'SUCCESS' if registry_check.status == 200 else 'FAILED - may need manual check' }}" + import_role: + name: registry # 4. Deploy MinIO (Object Storage) - - name: Optionally load MinIO secrets from vault - include_vars: - file: "{{ playbook_dir }}/../secrets/production.vault.yml" - no_log: yes - ignore_errors: yes - delegate_to: localhost - become: no - - - name: Set MinIO root password from vault or generate - set_fact: - minio_password: "{{ vault_minio_root_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation')) }}" - no_log: yes - - - name: Set MinIO root user from vault or use default - set_fact: - minio_user: "{{ vault_minio_root_user | default('minioadmin') }}" - - - name: Ensure MinIO stack directory exists - file: - path: "{{ stacks_base_path }}/minio" - state: directory - mode: '0755' - - - name: Create MinIO stack .env file - template: - src: "{{ playbook_dir }}/../templates/minio.env.j2" - dest: "{{ stacks_base_path }}/minio/.env" - owner: "{{ ansible_user }}" - group: "{{ ansible_user }}" - mode: '0600' - vars: - minio_root_user: "{{ minio_user }}" - minio_root_password: "{{ minio_password }}" - minio_api_domain: "{{ minio_api_domain }}" - minio_console_domain: "{{ minio_console_domain }}" - no_log: yes - - name: Deploy MinIO stack - community.docker.docker_compose_v2: - project_src: "{{ stacks_base_path }}/minio" - state: present - pull: always - register: minio_output - - - name: Wait for MinIO to be ready - wait_for: - timeout: "{{ wait_timeout }}" - when: minio_output.changed - - - name: Check MinIO logs for readiness - shell: docker compose logs minio 2>&1 | grep -Ei "(API:|WebUI:|MinIO Object Storage Server)" || true - args: - chdir: "{{ stacks_base_path }}/minio" - register: minio_logs - until: minio_logs.stdout != "" - retries: 6 - delay: 10 - changed_when: false - ignore_errors: yes - - - name: Verify MinIO health endpoint - uri: - url: "http://127.0.0.1:9000/minio/health/live" - method: GET - status_code: [200, 404, 502, 503] - timeout: 5 - register: minio_health_check - ignore_errors: yes - changed_when: false - - - name: Display MinIO status - debug: - msg: "MinIO health check: {{ 'SUCCESS' if minio_health_check.status == 200 else 'FAILED - Status: ' + (minio_health_check.status|string) }}" + import_role: + name: minio # 5. Deploy Gitea (CRITICAL - Git Server + MySQL + Redis) - name: Deploy Gitea stack - community.docker.docker_compose_v2: - project_src: "{{ stacks_base_path }}/gitea" - state: present - pull: always - register: gitea_output - - - name: Wait for Gitea to be ready - wait_for: - timeout: "{{ wait_timeout }}" - when: gitea_output.changed - - - name: Check Gitea logs for readiness - shell: docker compose logs gitea 2>&1 | grep -Ei "(Listen:|Server is running|Starting server)" || true - args: - chdir: "{{ stacks_base_path }}/gitea" - register: gitea_logs - until: gitea_logs.stdout != "" - retries: 12 - delay: 10 - changed_when: false - ignore_errors: yes + import_role: + name: gitea # 6. Deploy Monitoring (Portainer + Grafana + Prometheus) - - name: Optionally load monitoring secrets from vault - include_vars: - file: "{{ playbook_dir }}/../secrets/production.vault.yml" - no_log: yes - ignore_errors: yes - delegate_to: localhost - become: no - - - name: Set Grafana admin password from vault or generate - set_fact: - grafana_admin_password: "{{ vault_grafana_admin_password | default(lookup('password', '/dev/null length=25 chars=ascii_letters,digits')) }}" - - - name: Set Prometheus password from vault or generate - set_fact: - prometheus_password: "{{ vault_prometheus_password | default(lookup('password', '/dev/null length=25 chars=ascii_letters,digits')) }}" - - - name: Generate Prometheus BasicAuth hash - shell: | - docker run --rm httpd:alpine htpasswd -nbB admin "{{ prometheus_password }}" 2>/dev/null | cut -d ":" -f 2 - register: prometheus_auth_hash - changed_when: false - no_log: yes - - - name: Set Prometheus BasicAuth string - set_fact: - prometheus_auth: "admin:{{ prometheus_auth_hash.stdout }}" - - - name: Ensure monitoring stack directory exists - file: - path: "{{ stacks_base_path }}/monitoring" - state: directory - mode: '0755' - - - name: Create monitoring stack .env file - template: - src: "{{ playbook_dir }}/../templates/monitoring.env.j2" - dest: "{{ stacks_base_path }}/monitoring/.env" - owner: "{{ ansible_user }}" - group: "{{ ansible_user }}" - mode: '0600' - no_log: yes - - name: Deploy Monitoring stack - community.docker.docker_compose_v2: - project_src: "{{ stacks_base_path }}/monitoring" - state: present - pull: always - register: monitoring_output - - - name: Wait for Monitoring to be ready - wait_for: - timeout: "{{ wait_timeout }}" - when: monitoring_output.changed + import_role: + name: monitoring # Verification - name: List all running containers @@ -345,180 +94,42 @@ msg: "Gitea HTTPS check: {{ 'SUCCESS' if gitea_http_check.status == 200 else 'FAILED - Status: ' + (gitea_http_check.status|string) }}" # 7. Deploy Application Stack - - name: Optionally load application secrets from vault - include_vars: - file: "{{ playbook_dir }}/../secrets/production.vault.yml" - no_log: yes - ignore_errors: yes - delegate_to: localhost - become: no - - - name: Check if PostgreSQL .env exists - stat: - path: "{{ stacks_base_path }}/postgresql/.env" - register: postgres_env_file - changed_when: false - - - name: Extract PostgreSQL password from .env file - shell: "grep '^POSTGRES_PASSWORD=' {{ stacks_base_path }}/postgresql/.env 2>/dev/null | cut -d'=' -f2- || echo ''" - register: postgres_password_from_file - changed_when: false - failed_when: false - when: postgres_env_file.stat.exists - no_log: yes - - - name: Set application database password (from file, vault, or generate) - set_fact: - app_db_password: "{{ postgres_password_from_file.stdout if (postgres_env_file.stat.exists and postgres_password_from_file.stdout != '') else (vault_db_root_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation'))) }}" - no_log: yes - - - name: Set application redis password from vault or generate - set_fact: - app_redis_password: "{{ vault_redis_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation')) }}" - - - name: Ensure application stack directory exists - file: - path: "{{ stacks_base_path }}/application" - state: directory - mode: '0755' - - - name: Check if application stack docker-compose.yml exists locally - stat: - path: "{{ playbook_dir }}/../../stacks/application/docker-compose.yml" - register: app_compose_local - delegate_to: localhost - become: no - - - name: Copy application stack docker-compose.yml to server - copy: - src: "{{ playbook_dir }}/../../stacks/application/docker-compose.yml" - dest: "{{ stacks_base_path }}/application/docker-compose.yml" - owner: "{{ ansible_user }}" - group: "{{ ansible_user }}" - mode: '0644' - when: app_compose_local.stat.exists - - - name: Check if application stack nginx directory exists locally - stat: - path: "{{ playbook_dir }}/../../stacks/application/nginx/" - register: app_nginx_local - delegate_to: localhost - become: no - - - name: Copy application stack nginx configuration to server - copy: - src: "{{ playbook_dir }}/../../stacks/application/nginx/" - dest: "{{ stacks_base_path }}/application/nginx/" - owner: "{{ ansible_user }}" - group: "{{ ansible_user }}" - mode: '0644' - when: app_nginx_local.stat.exists - - - name: Create application stack .env file - template: - src: "{{ playbook_dir }}/../templates/application.env.j2" - dest: "{{ stacks_base_path }}/application/.env" - owner: "{{ ansible_user }}" - group: "{{ ansible_user }}" - mode: '0600' - vars: - db_password: "{{ app_db_password }}" - db_user: "{{ db_user | default(db_user_default) }}" - db_name: "{{ db_name | default(db_name_default) }}" - redis_password: "{{ app_redis_password }}" - app_domain: "{{ app_domain }}" - no_log: yes - - - name: Deploy Application stack - community.docker.docker_compose_v2: - project_src: "{{ stacks_base_path }}/application" - state: present - pull: always - register: application_output - - - name: Wait for Application to be ready - wait_for: - timeout: "{{ wait_timeout }}" - when: application_output.changed - - - name: Wait for application containers to be healthy - pause: - seconds: 30 - when: application_output.changed - - - name: Check application container health status - shell: | - docker compose -f {{ stacks_base_path }}/application/docker-compose.yml ps --format json | jq -r '.[] | select(.Health != "healthy" and .Health != "" and .Health != "starting") | "\(.Name): \(.Health)"' || echo "All healthy or no health checks" - args: - executable: /bin/bash - register: app_health_status - changed_when: false - ignore_errors: yes + - name: Deploy Application Stack + import_role: + name: application - name: Display application health status debug: - msg: "Application health: {{ app_health_status.stdout if app_health_status.stdout != '' else 'All services healthy or starting' }}" - - - name: Wait for app container to be ready before migration - wait_for: - timeout: 60 - when: application_output.changed - - - name: Check if app container is running - shell: | - docker compose -f {{ stacks_base_path }}/application/docker-compose.yml ps app | grep -q "Up" || exit 1 - args: - executable: /bin/bash - register: app_container_running - changed_when: false - failed_when: false - when: application_output.changed - - - name: Run database migrations - shell: | - docker compose -f {{ stacks_base_path }}/application/docker-compose.yml exec -T app php console.php db:migrate - args: - executable: /bin/bash - register: migration_result - changed_when: true - failed_when: false - ignore_errors: yes - when: application_output.changed and app_container_running.rc == 0 + msg: "Application health: {{ application_health_output if application_health_output != '' else 'All services healthy or starting' }}" - name: Display migration result debug: msg: | Migration Result: - {{ migration_result.stdout if migration_result.rc == 0 else 'Migration may have failed - check logs with: docker compose -f ' + stacks_base_path + '/application/docker-compose.yml logs app' }} - when: application_output.changed - - - name: Verify application accessibility via HTTPS - uri: - url: "{{ health_check_url }}" - method: GET - validate_certs: no - status_code: [200, 404, 502, 503] - timeout: 10 - register: app_health_check - ignore_errors: yes - when: application_output.changed + {{ application_migration_stdout if application_migration_stdout != '' else 'Migration may have failed - check logs with: docker compose -f ' + application_stack_dest + '/docker-compose.yml logs app' }} + when: application_stack_changed and application_run_migrations - name: Display application accessibility status debug: - msg: "Application health check: {{ 'SUCCESS (HTTP ' + (app_health_check.status|string) + ')' if app_health_check.status == 200 else 'FAILED or not ready yet (HTTP ' + (app_health_check.status|string) + ')' }}" - when: application_output.changed + msg: >- + Application health check: {{ + 'SUCCESS (HTTP ' + (application_healthcheck_status | string) + ')' + if application_healthcheck_status == 200 else + 'FAILED or not ready yet (HTTP ' + (application_healthcheck_status | string) + ')' + }} + when: application_stack_changed and application_healthcheck_url | length > 0 - name: Summary debug: msg: - "=== Infrastructure Deployment Complete ===" - - "Traefik: {{ 'Deployed' if traefik_output.changed else 'Already running' }}" - - "PostgreSQL: {{ 'Deployed' if postgres_output.changed else 'Already running' }}" - - "Docker Registry: {{ 'Deployed' if registry_output.changed else 'Already running' }}" - - "MinIO: {{ 'Deployed' if minio_output.changed else 'Already running' }}" - - "Gitea: {{ 'Deployed' if gitea_output.changed else 'Already running' }}" - - "Monitoring: {{ 'Deployed' if monitoring_output.changed else 'Already running' }}" - - "Application: {{ 'Deployed' if application_output.changed else 'Already running' }}" + - "Traefik: {{ 'Deployed' if traefik_stack_changed else 'Already running' }}" + - "PostgreSQL: {{ 'Deployed' if postgresql_stack_changed else 'Already running' }}" + - "Docker Registry: {{ 'Deployed' if registry_stack_changed else 'Already running' }}" + - "MinIO: {{ 'Deployed' if minio_stack_changed else 'Already running' }}" + - "Gitea: {{ 'Deployed' if gitea_stack_changed else 'Already running' }}" + - "Monitoring: {{ 'Deployed' if monitoring_stack_changed else 'Already running' }}" + - "Application: {{ 'Deployed' if application_stack_changed else 'Already running' }}" - "" - "Next Steps:" - "1. Access Gitea at: https://{{ gitea_domain }}" diff --git a/deployment/ansible/roles/application/defaults/main.yml b/deployment/ansible/roles/application/defaults/main.yml new file mode 100644 index 00000000..d054a68f --- /dev/null +++ b/deployment/ansible/roles/application/defaults/main.yml @@ -0,0 +1,34 @@ +--- +# Source path for application stack files on the control node +application_stack_src: "{{ role_path }}/../../stacks/application" + +# Destination path on the target host (defaults to configured app_stack_path) +application_stack_dest: "{{ app_stack_path | default(stacks_base_path + '/application') }}" + +# Template used to generate the application .env file +application_env_template: "{{ role_path }}/../../templates/application.env.j2" + +# Optional vault file containing secrets (loaded if present) +application_vault_file: "{{ role_path }}/../../secrets/production.vault.yml" + +# Whether to synchronize stack files from repository +application_sync_files: true + +# Compose recreate strategy ("auto", "always", "never") +application_compose_recreate: "auto" + +# Whether to remove orphaned containers during compose up +application_remove_orphans: false + +# Whether to run database migrations after (re)deploying the stack +application_run_migrations: true + +# Optional health check URL to verify after deployment +application_healthcheck_url: "{{ health_check_url | default('') }}" + +# Timeout used for waits in this role +application_wait_timeout: "{{ wait_timeout | default(60) }}" +application_wait_interval: 5 + +# Command executed inside the app container to run migrations +application_migration_command: "php console.php db:migrate" diff --git a/deployment/ansible/roles/application/tasks/deploy.yml b/deployment/ansible/roles/application/tasks/deploy.yml new file mode 100644 index 00000000..d8b4f40f --- /dev/null +++ b/deployment/ansible/roles/application/tasks/deploy.yml @@ -0,0 +1,69 @@ +--- +- name: Deploy application stack + community.docker.docker_compose_v2: + project_src: "{{ application_stack_dest }}" + state: present + pull: always + recreate: "{{ application_compose_recreate }}" + remove_orphans: "{{ application_remove_orphans | bool }}" + register: application_compose_result + +- name: Wait for application container to report Up + shell: | + docker compose -f {{ application_stack_dest }}/docker-compose.yml ps app | grep -Eiq "Up|running" + register: application_app_running + changed_when: false + until: application_app_running.rc == 0 + retries: "{{ ((application_wait_timeout | int) + (application_wait_interval | int) - 1) // (application_wait_interval | int) }}" + delay: "{{ application_wait_interval | int }}" + when: application_compose_result.changed + +- name: Ensure app container is running before migrations + shell: | + docker compose -f {{ application_stack_dest }}/docker-compose.yml ps app | grep -Eiq "Up|running" + args: + executable: /bin/bash + register: application_app_container_running + changed_when: false + failed_when: false + when: application_compose_result.changed + +- name: Run database migrations + shell: | + docker compose -f {{ application_stack_dest }}/docker-compose.yml exec -T app {{ application_migration_command }} + args: + executable: /bin/bash + register: application_migration_result + changed_when: true + failed_when: false + ignore_errors: yes + when: + - application_run_migrations + - application_compose_result.changed + - application_app_container_running.rc == 0 + +- name: Collect application container status + shell: docker compose -f {{ application_stack_dest }}/docker-compose.yml ps + register: application_ps + changed_when: false + ignore_errors: yes + +- name: Perform application health check + uri: + url: "{{ application_healthcheck_url }}" + method: GET + validate_certs: no + status_code: [200, 404, 502, 503] + timeout: 10 + register: application_healthcheck_result + ignore_errors: yes + when: + - application_healthcheck_url | length > 0 + - application_compose_result.changed + +- name: Set application role summary facts + set_fact: + application_stack_changed: "{{ application_compose_result.changed | default(false) }}" + application_health_output: "{{ application_ps.stdout | default('') }}" + application_healthcheck_status: "{{ application_healthcheck_result.status | default('unknown') }}" + application_migration_stdout: "{{ application_migration_result.stdout | default('') }}" diff --git a/deployment/ansible/roles/application/tasks/main.yml b/deployment/ansible/roles/application/tasks/main.yml new file mode 100644 index 00000000..a22d9423 --- /dev/null +++ b/deployment/ansible/roles/application/tasks/main.yml @@ -0,0 +1,7 @@ +--- +- name: Synchronize application stack files + include_tasks: sync.yml + when: application_sync_files | bool + +- name: Deploy application stack + include_tasks: deploy.yml diff --git a/deployment/ansible/roles/application/tasks/sync.yml b/deployment/ansible/roles/application/tasks/sync.yml new file mode 100644 index 00000000..ea309f29 --- /dev/null +++ b/deployment/ansible/roles/application/tasks/sync.yml @@ -0,0 +1,94 @@ +--- +- name: Ensure application stack destination directory exists + file: + path: "{{ application_stack_dest }}" + state: directory + mode: '0755' + +- name: Check if vault file exists locally + stat: + path: "{{ application_vault_file }}" + delegate_to: localhost + register: application_vault_stat + become: no + +- name: Optionally load application secrets from vault + include_vars: + file: "{{ application_vault_file }}" + when: application_vault_stat.stat.exists + no_log: yes + delegate_to: localhost + become: no + +- name: Check if PostgreSQL .env exists on target host + stat: + path: "{{ stacks_base_path }}/postgresql/.env" + register: application_postgres_env_file + changed_when: false + +- name: Extract PostgreSQL password from .env file + shell: "grep '^POSTGRES_PASSWORD=' {{ stacks_base_path }}/postgresql/.env 2>/dev/null | cut -d'=' -f2- || echo ''" + register: application_postgres_password + changed_when: false + failed_when: false + when: application_postgres_env_file.stat.exists + no_log: yes + +- name: Determine application database password + set_fact: + application_db_password: >- + {{ (application_postgres_env_file.stat.exists and application_postgres_password.stdout != '') | + ternary(application_postgres_password.stdout, + vault_db_root_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation'))) }} + no_log: yes + +- name: Determine application redis password + set_fact: + application_redis_password: "{{ vault_redis_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation')) }}" + no_log: yes + +- name: Check if application docker-compose source exists locally + stat: + path: "{{ application_stack_src }}/docker-compose.yml" + delegate_to: localhost + register: application_compose_src + become: no + +- name: Copy application docker-compose to target host + copy: + src: "{{ application_stack_src }}/docker-compose.yml" + dest: "{{ application_stack_dest }}/docker-compose.yml" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: '0644' + when: application_compose_src.stat.exists + +- name: Check if nginx configuration exists locally + stat: + path: "{{ application_stack_src }}/nginx" + delegate_to: localhost + register: application_nginx_src + become: no + +- name: Synchronize nginx configuration + copy: + src: "{{ application_stack_src }}/nginx/" + dest: "{{ application_stack_dest }}/nginx/" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: '0644' + when: application_nginx_src.stat.exists + +- name: Render application environment file + template: + src: "{{ application_env_template }}" + dest: "{{ application_stack_dest }}/.env" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: '0600' + vars: + db_password: "{{ application_db_password }}" + db_user: "{{ db_user | default(db_user_default) }}" + db_name: "{{ db_name | default(db_name_default) }}" + redis_password: "{{ application_redis_password }}" + app_domain: "{{ app_domain }}" diff --git a/deployment/ansible/roles/gitea/defaults/main.yml b/deployment/ansible/roles/gitea/defaults/main.yml new file mode 100644 index 00000000..2a2efcb7 --- /dev/null +++ b/deployment/ansible/roles/gitea/defaults/main.yml @@ -0,0 +1,4 @@ +--- +gitea_stack_path: "{{ stacks_base_path }}/gitea" +gitea_wait_timeout: "{{ wait_timeout | default(60) }}" +gitea_wait_interval: 5 diff --git a/deployment/ansible/roles/gitea/tasks/main.yml b/deployment/ansible/roles/gitea/tasks/main.yml new file mode 100644 index 00000000..c758a947 --- /dev/null +++ b/deployment/ansible/roles/gitea/tasks/main.yml @@ -0,0 +1,35 @@ +--- +- name: Deploy Gitea stack + community.docker.docker_compose_v2: + project_src: "{{ gitea_stack_path }}" + state: present + pull: always + register: gitea_compose_result + +- name: Check Gitea container status + shell: | + docker compose -f {{ gitea_stack_path }}/docker-compose.yml ps gitea | grep -Eiq "Up|running" + register: gitea_state + changed_when: false + until: gitea_state.rc == 0 + retries: "{{ ((gitea_wait_timeout | int) + (gitea_wait_interval | int) - 1) // (gitea_wait_interval | int) }}" + delay: "{{ gitea_wait_interval | int }}" + failed_when: gitea_state.rc != 0 + when: not ansible_check_mode + +- name: Check Gitea logs for readiness + shell: docker compose logs gitea 2>&1 | grep -Ei "(Listen:|Server is running|Starting server)" || true + args: + chdir: "{{ gitea_stack_path }}" + register: gitea_logs + until: gitea_logs.stdout != "" + retries: 12 + delay: 10 + changed_when: false + failed_when: false + when: not ansible_check_mode + +- name: Record Gitea deployment facts + set_fact: + gitea_stack_changed: "{{ gitea_compose_result.changed | default(false) }}" + gitea_log_hint: "{{ gitea_logs.stdout | default('') }}" diff --git a/deployment/ansible/roles/minio/defaults/main.yml b/deployment/ansible/roles/minio/defaults/main.yml new file mode 100644 index 00000000..6109abb9 --- /dev/null +++ b/deployment/ansible/roles/minio/defaults/main.yml @@ -0,0 +1,6 @@ +--- +minio_stack_path: "{{ stacks_base_path }}/minio" +minio_wait_timeout: "{{ wait_timeout | default(60) }}" +minio_wait_interval: 5 +minio_env_template: "{{ role_path }}/../../templates/minio.env.j2" +minio_vault_file: "{{ role_path }}/../../secrets/production.vault.yml" diff --git a/deployment/ansible/roles/minio/tasks/main.yml b/deployment/ansible/roles/minio/tasks/main.yml new file mode 100644 index 00000000..fa9e7230 --- /dev/null +++ b/deployment/ansible/roles/minio/tasks/main.yml @@ -0,0 +1,90 @@ +--- +- name: Check if MinIO vault file exists + stat: + path: "{{ minio_vault_file }}" + delegate_to: localhost + register: minio_vault_stat + become: no + +- name: Optionally load MinIO secrets from vault + include_vars: + file: "{{ minio_vault_file }}" + when: minio_vault_stat.stat.exists + no_log: yes + delegate_to: localhost + become: no + +- name: Set MinIO root password from vault or generate + set_fact: + minio_root_password: "{{ vault_minio_root_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation')) }}" + no_log: yes + +- name: Set MinIO root user from vault or use default + set_fact: + minio_root_user: "{{ vault_minio_root_user | default('minioadmin') }}" + no_log: yes + +- name: Ensure MinIO stack directory exists + file: + path: "{{ minio_stack_path }}" + state: directory + mode: '0755' + +- name: Create MinIO stack .env file + template: + src: "{{ minio_env_template }}" + dest: "{{ minio_stack_path }}/.env" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: '0600' + +- name: Deploy MinIO stack + community.docker.docker_compose_v2: + project_src: "{{ minio_stack_path }}" + state: present + pull: always + register: minio_compose_result + +- name: Check MinIO container status + shell: | + docker compose -f {{ minio_stack_path }}/docker-compose.yml ps minio | grep -Eiq "Up|running" + register: minio_state + changed_when: false + until: minio_state.rc == 0 + retries: "{{ ((minio_wait_timeout | int) + (minio_wait_interval | int) - 1) // (minio_wait_interval | int) }}" + delay: "{{ minio_wait_interval | int }}" + failed_when: minio_state.rc != 0 + when: not ansible_check_mode + +- name: Check MinIO logs for readiness + shell: docker compose logs minio 2>&1 | grep -Ei "(API:|WebUI:|MinIO Object Storage Server)" || true + args: + chdir: "{{ minio_stack_path }}" + register: minio_logs + until: minio_logs.stdout != "" + retries: 6 + delay: 10 + changed_when: false + failed_when: false + when: not ansible_check_mode + +- name: Verify MinIO health endpoint + uri: + url: "http://127.0.0.1:9000/minio/health/live" + method: GET + status_code: [200, 404, 502, 503] + timeout: 5 + register: minio_health_check + ignore_errors: yes + changed_when: false + when: not ansible_check_mode + +- name: Display MinIO status + debug: + msg: "MinIO health check: {{ 'SUCCESS' if minio_health_check.status == 200 else 'FAILED - Status: ' + (minio_health_check.status|string) }}" + when: not ansible_check_mode + +- name: Record MinIO deployment facts + set_fact: + minio_stack_changed: "{{ minio_compose_result.changed | default(false) }}" + minio_health_status: "{{ minio_health_check.status | default('unknown') }}" diff --git a/deployment/ansible/roles/monitoring/defaults/main.yml b/deployment/ansible/roles/monitoring/defaults/main.yml new file mode 100644 index 00000000..5420d9f3 --- /dev/null +++ b/deployment/ansible/roles/monitoring/defaults/main.yml @@ -0,0 +1,5 @@ +--- +monitoring_stack_path: "{{ stacks_base_path }}/monitoring" +monitoring_wait_timeout: "{{ wait_timeout | default(60) }}" +monitoring_env_template: "{{ role_path }}/../../templates/monitoring.env.j2" +monitoring_vault_file: "{{ role_path }}/../../secrets/production.vault.yml" diff --git a/deployment/ansible/roles/monitoring/tasks/main.yml b/deployment/ansible/roles/monitoring/tasks/main.yml new file mode 100644 index 00000000..146c58a6 --- /dev/null +++ b/deployment/ansible/roles/monitoring/tasks/main.yml @@ -0,0 +1,68 @@ +--- +- name: Check if monitoring vault file exists + stat: + path: "{{ monitoring_vault_file }}" + delegate_to: localhost + register: monitoring_vault_stat + become: no + +- name: Optionally load monitoring secrets from vault + include_vars: + file: "{{ monitoring_vault_file }}" + when: monitoring_vault_stat.stat.exists + no_log: yes + delegate_to: localhost + become: no + +- name: Set Grafana admin password from vault or generate + set_fact: + grafana_admin_password: "{{ vault_grafana_admin_password | default(lookup('password', '/dev/null length=25 chars=ascii_letters,digits')) }}" + no_log: yes + +- name: Set Prometheus password from vault or generate + set_fact: + prometheus_password: "{{ vault_prometheus_password | default(lookup('password', '/dev/null length=25 chars=ascii_letters,digits')) }}" + no_log: yes + +- name: Generate Prometheus BasicAuth hash + shell: | + docker run --rm httpd:alpine htpasswd -nbB admin "{{ prometheus_password }}" 2>/dev/null | cut -d ":" -f 2 + register: prometheus_auth_hash + changed_when: false + no_log: yes + +- name: Set Prometheus BasicAuth string + set_fact: + prometheus_auth: "admin:{{ prometheus_auth_hash.stdout }}" + no_log: yes + +- name: Ensure monitoring stack directory exists + file: + path: "{{ monitoring_stack_path }}" + state: directory + mode: '0755' + +- name: Create monitoring stack .env file + template: + src: "{{ monitoring_env_template }}" + dest: "{{ monitoring_stack_path }}/.env" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: '0600' + no_log: yes + +- name: Deploy Monitoring stack + community.docker.docker_compose_v2: + project_src: "{{ monitoring_stack_path }}" + state: present + pull: always + register: monitoring_compose_result + +- name: Wait for Monitoring to be ready + wait_for: + timeout: "{{ monitoring_wait_timeout }}" + when: monitoring_compose_result.changed + +- name: Record monitoring deployment facts + set_fact: + monitoring_stack_changed: "{{ monitoring_compose_result.changed | default(false) }}" diff --git a/deployment/ansible/roles/postgresql/defaults/main.yml b/deployment/ansible/roles/postgresql/defaults/main.yml new file mode 100644 index 00000000..cab73a39 --- /dev/null +++ b/deployment/ansible/roles/postgresql/defaults/main.yml @@ -0,0 +1,4 @@ +--- +postgresql_stack_path: "{{ stacks_base_path }}/postgresql" +postgresql_wait_timeout: "{{ wait_timeout | default(60) }}" +postgresql_wait_interval: 5 diff --git a/deployment/ansible/roles/postgresql/tasks/main.yml b/deployment/ansible/roles/postgresql/tasks/main.yml new file mode 100644 index 00000000..59c0e101 --- /dev/null +++ b/deployment/ansible/roles/postgresql/tasks/main.yml @@ -0,0 +1,23 @@ +--- +- name: Deploy PostgreSQL stack + community.docker.docker_compose_v2: + project_src: "{{ postgresql_stack_path }}" + state: present + pull: always + register: postgresql_compose_result + +- name: Check PostgreSQL container status + shell: | + docker compose -f {{ postgresql_stack_path }}/docker-compose.yml ps postgres | grep -Eiq "Up|running" + register: postgresql_state + changed_when: false + until: postgresql_state.rc == 0 + retries: "{{ ((postgresql_wait_timeout | int) + (postgresql_wait_interval | int) - 1) // (postgresql_wait_interval | int) }}" + delay: "{{ postgresql_wait_interval | int }}" + failed_when: postgresql_state.rc != 0 + when: not ansible_check_mode + +- name: Record PostgreSQL deployment facts + set_fact: + postgresql_stack_changed: "{{ postgresql_compose_result.changed | default(false) }}" + postgresql_log_hint: "" diff --git a/deployment/ansible/roles/registry/defaults/main.yml b/deployment/ansible/roles/registry/defaults/main.yml new file mode 100644 index 00000000..40f41608 --- /dev/null +++ b/deployment/ansible/roles/registry/defaults/main.yml @@ -0,0 +1,5 @@ +--- +registry_stack_path: "{{ stacks_base_path }}/registry" +registry_wait_timeout: "{{ wait_timeout | default(60) }}" +registry_wait_interval: 5 +registry_vault_file: "{{ role_path }}/../../secrets/production.vault.yml" diff --git a/deployment/ansible/roles/registry/tasks/main.yml b/deployment/ansible/roles/registry/tasks/main.yml new file mode 100644 index 00000000..1552d895 --- /dev/null +++ b/deployment/ansible/roles/registry/tasks/main.yml @@ -0,0 +1,115 @@ +--- +- name: Ensure Registry auth directory exists + file: + path: "{{ registry_auth_path }}" + state: directory + mode: '0755' + become: yes + +- name: Check if registry vault file exists + stat: + path: "{{ registry_vault_file }}" + delegate_to: localhost + register: registry_vault_stat + become: no + +- name: Optionally load registry credentials from vault + include_vars: + file: "{{ registry_vault_file }}" + when: registry_vault_stat.stat.exists + no_log: yes + delegate_to: localhost + become: no + register: registry_vault_vars + failed_when: false + +- name: Fail if registry vault decryption failed + fail: + msg: > + Failed to decrypt {{ registry_vault_file }}. + Provide a valid vault password (e.g. via --vault-password-file) or update docker_registry_password_default. + when: + - not ansible_check_mode + - registry_vault_stat.stat.exists + - registry_vault_vars is defined + - registry_vault_vars.failed | default(false) + +- name: Set registry credentials from vault or defaults or generate + set_fact: + registry_username: "{{ vault_docker_registry_username | default(docker_registry_username_default) }}" + registry_password: >- + {{ + vault_docker_registry_password + | default(docker_registry_password_default) + | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits')) + }} + no_log: true + +- name: Create Registry htpasswd file if missing + shell: | + docker run --rm --entrypoint htpasswd httpd:2 -Bbn {{ registry_username }} {{ registry_password }} > {{ registry_auth_path }}/htpasswd + chmod 644 {{ registry_auth_path }}/htpasswd + args: + executable: /bin/bash + creates: "{{ registry_auth_path }}/htpasswd" + become: yes + no_log: true + when: not ansible_check_mode + +- name: Deploy Docker Registry stack + community.docker.docker_compose_v2: + project_src: "{{ registry_stack_path }}" + state: present + pull: always + register: registry_compose_result + +- name: Wait for Docker Registry to be ready + wait_for: + timeout: "{{ registry_wait_timeout }}" + when: registry_compose_result.changed + +- name: Check Registry container status + shell: | + docker compose -f {{ registry_stack_path }}/docker-compose.yml ps registry | grep -Eiq "Up|running" + register: registry_state + changed_when: false + until: registry_state.rc == 0 + retries: "{{ ((registry_wait_timeout | int) + (registry_wait_interval | int) - 1) // (registry_wait_interval | int) }}" + delay: "{{ registry_wait_interval | int }}" + failed_when: registry_state.rc != 0 + when: not ansible_check_mode + +- name: Check Registry logs for readiness + shell: docker compose logs registry 2>&1 | grep -Ei "(listening on|listening at|http server)" || true + args: + chdir: "{{ registry_stack_path }}" + register: registry_logs + until: registry_logs.stdout != "" + retries: 6 + delay: 10 + changed_when: false + failed_when: false + when: not ansible_check_mode + +- name: Verify Registry is accessible + uri: + url: "http://127.0.0.1:5000/v2/_catalog" + user: "{{ registry_username }}" + password: "{{ registry_password }}" + status_code: 200 + timeout: 5 + register: registry_check + ignore_errors: yes + changed_when: false + no_log: true + when: not ansible_check_mode + +- name: Display Registry status + debug: + msg: "Registry accessibility: {{ 'SUCCESS' if registry_check.status == 200 else 'FAILED - may need manual check' }}" + when: not ansible_check_mode + +- name: Record registry deployment facts + set_fact: + registry_stack_changed: "{{ registry_compose_result.changed | default(false) }}" + registry_access_status: "{{ registry_check.status | default('unknown') }}" diff --git a/deployment/ansible/roles/traefik/defaults/main.yml b/deployment/ansible/roles/traefik/defaults/main.yml new file mode 100644 index 00000000..d28df11a --- /dev/null +++ b/deployment/ansible/roles/traefik/defaults/main.yml @@ -0,0 +1,4 @@ +--- +traefik_stack_path: "{{ stacks_base_path }}/traefik" +traefik_wait_timeout: "{{ wait_timeout | default(60) }}" +traefik_wait_interval: 5 diff --git a/deployment/ansible/roles/traefik/tasks/main.yml b/deployment/ansible/roles/traefik/tasks/main.yml new file mode 100644 index 00000000..d2700e48 --- /dev/null +++ b/deployment/ansible/roles/traefik/tasks/main.yml @@ -0,0 +1,23 @@ +--- +- name: Deploy Traefik stack + community.docker.docker_compose_v2: + project_src: "{{ traefik_stack_path }}" + state: present + pull: always + register: traefik_compose_result + +- name: Check Traefik container status + shell: | + docker compose -f {{ traefik_stack_path }}/docker-compose.yml ps traefik | grep -Eiq "Up|running" + register: traefik_state + changed_when: false + until: traefik_state.rc == 0 + retries: "{{ ((traefik_wait_timeout | int) + (traefik_wait_interval | int) - 1) // (traefik_wait_interval | int) }}" + delay: "{{ traefik_wait_interval | int }}" + failed_when: traefik_state.rc != 0 + when: not ansible_check_mode + +- name: Record Traefik deployment facts + set_fact: + traefik_stack_changed: "{{ traefik_compose_result.changed | default(false) }}" + traefik_log_hint: "" diff --git a/deployment/docs/README.md b/deployment/docs/README.md new file mode 100644 index 00000000..98afba00 --- /dev/null +++ b/deployment/docs/README.md @@ -0,0 +1,198 @@ +# Deployment Dokumentation - Index + +**Stand:** 2025-11-01 +**Status:** ? Vollst?ndige Dokumentation vorhanden + +--- + +## ?? Schnellstart + +### F?r schnellen Einstieg + +1. **[quick-start.md](guides/quick-start.md)** ? + - Schnellstart-Guide + - Pipeline-Status pr?fen + - Troubleshooting Quick Reference + +2. **[code-change-workflow.md](guides/code-change-workflow.md)** + - Wie Code?nderungen gepusht werden + - Automatisches vs. manuelles Deployment + - Branching-Strategien + - Beispiel-Workflows + +--- + +## ?? Detaillierte Guides + +### Setup & Konfiguration + +3. **[setup-guide.md](guides/setup-guide.md)** + - Kompletter Setup-Guide von Anfang bis Ende + - Infrastructure Deployment + - Gitea Runner Setup + - Secrets Konfiguration + - Schritt-f?r-Schritt Anleitung + +4. **[deployment-commands.md](guides/deployment-commands.md)** + - Command-Referenz f?r Deployment + - Ansible Playbook Befehle + - Docker Compose Kommandos + - Troubleshooting Kommandos + +5. **[vault-password.md](guides/vault-password.md)** + - Ansible Vault Password Dokumentation + - Setup und Verwaltung + - CI/CD Integration + - Best Practices + +### Deployment-Prozess + +6. **[application-stack.md](reference/application-stack.md)** + - Detaillierter Deployment-Ablauf Schritt f?r Schritt + - Was passiert bei jedem Deployment + - Container-Neustart Details + - Rollback-Prozess + - Troubleshooting + +--- + +## ?? Status & ?bersicht + +### Projekt-Status + +7. **[deployment-summary.md](status/deployment-summary.md)** + - Was ist fertig? + - Was fehlt noch? + - Completion Rate + - N?chste Schritte + +8. **[deployment-todo.md](status/deployment-todo.md)** + - Aktuelle TODO-Liste + - Priorisierte Reihenfolge + - Quick Checklist + +### CI/CD Pipeline + +9. **[ci-cd-status.md](status/ci-cd-status.md)** + - Aktueller CI/CD Status + - Secrets-?bersicht + - Runner-Status + - Checkliste f?r Completion + - Troubleshooting + +### Verbesserungen & Planung + +10. **[improvements.md](status/improvements.md)** + - Verbesserungsvorschl?ge + - Refactoring-Ideen + - Architektur-?berlegungen + +11. **[next-steps.md](status/next-steps.md)** + - Geplante n?chste Schritte + - Roadmap + - Priorit?ten + +--- + +## ?? Stack-spezifische Dokumentation + +### Infrastructure Stacks + +- **[stacks/traefik/README.md](../stacks/traefik/README.md)** - Reverse Proxy & SSL +- **[stacks/postgresql/README.md](../stacks/postgresql/README.md)** - Database mit Backups +- **[stacks/registry/README.md](../stacks/registry/README.md)** - Private Docker Registry +- **[stacks/gitea/README.md](../stacks/gitea/README.md)** - Git Server & CI/CD +- **[stacks/monitoring/README.md](../stacks/monitoring/README.md)** - Monitoring Tools + +### Application Stack + +- **[stacks/application/README.md](../stacks/application/README.md)** - Application Stack Details +- **[ansible/README.md](../ansible/README.md)** - Ansible Playbooks Dokumentation + +### CI/CD + +- **[gitea-runner/README.md](../gitea-runner/README.md)** - Gitea Runner Setup +- **[.gitea/workflows/production-deploy.yml](../../.gitea/workflows/production-deploy.yml)** - Haupt-Deployment-Pipeline + +--- + +## ?? Security & VPN + +- **[vault-password.md](guides/vault-password.md)** - Ansible Vault Password Dokumentation +- **[docs/WIREGUARD-SETUP.md](../../docs/WIREGUARD-SETUP.md)** - WireGuard VPN Setup +- **[ansible/playbooks/README-WIREGUARD.md](../ansible/playbooks/README-WIREGUARD.md)** - WireGuard Ansible Playbooks + +--- + +## ?? Troubleshooting + +### Workflow-Probleme + +- **[workflow-troubleshooting.md](reference/workflow-troubleshooting.md)** - Workflow Troubleshooting +- **[quick-start.md](guides/quick-start.md)** - Troubleshooting Quick Reference + +### Test-Dokumentation + +- **[test-results.md](tests/test-results.md)** - Test Ergebnisse +- **[git-deployment-test.md](tests/git-deployment-test.md)** - Git Deployment Tests +- **[git-deployment-issue.md](tests/git-deployment-issue.md)** - Git Deployment Issues +- **[test-git-deployment.md](tests/test-git-deployment.md)** - Git Deployment Test Guide +- **[quick-test.md](tests/quick-test.md)** - Quick Tests +- **[recommended-test-flow.md](tests/recommended-test-flow.md)** - Empfohlene Test-Workflows + +--- + +## ?? Historie & Logs + +- **[cleanup-log.md](history/cleanup-log.md)** - Cleanup Log +- **[cleanup-summary.md](history/cleanup-summary.md)** - Cleanup Zusammenfassung + +--- + +## ?? Haupt-Dokumentation + +- **[README.md](../README.md)** - Haupt-Dokumentation & Architektur-?bersicht + +--- + +## ?? Empfohlene Lesereihenfolge + +### F?r neue Nutzer + +1. **[quick-start.md](guides/quick-start.md)** - Schneller ?berblick +2. **[code-change-workflow.md](guides/code-change-workflow.md)** - Code deployen lernen +3. **[deployment-summary.md](status/deployment-summary.md)** - Projekt-Status verstehen + +### F?r Deployment-Verst?ndnis + +1. **[application-stack.md](reference/application-stack.md)** - Wie Deployment funktioniert +2. **[ci-cd-status.md](status/ci-cd-status.md)** - CI/CD Pipeline verstehen +3. **[setup-guide.md](guides/setup-guide.md)** - Komplette Setup-Anleitung + +### F?r Troubleshooting + +1. **[quick-start.md](guides/quick-start.md)** - Quick Troubleshooting +2. **[workflow-troubleshooting.md](reference/workflow-troubleshooting.md)** - Workflow-Probleme +3. Stack-spezifische READMEs f?r Details + +--- + +## ?? Dokumentations-Standards + +**Alle Dokumentationsdateien:** +- Verwenden Markdown-Format +- Haben klare ?berschriften-Struktur +- Enthalten Code-Beispiele +- Haben Troubleshooting-Abschnitte (wenn relevant) +- Verlinken zu verwandten Dokumenten + +**Standards:** +- ? Beispiele sind ausf?hrbar +- ? Pfade sind absolut oder relativ klar +- ? Screenshots/Links sind aktuell +- ? Status ist klar markiert (?/??/?) + +--- + +**Letzte Aktualisierung:** 2025-11-01 +**Status:** ? Dokumentation vollst?ndig diff --git a/deployment/docs/RESTRUCTURE_PROPOSAL.md b/deployment/docs/RESTRUCTURE_PROPOSAL.md new file mode 100644 index 00000000..4273b03f --- /dev/null +++ b/deployment/docs/RESTRUCTURE_PROPOSAL.md @@ -0,0 +1,223 @@ +# Dokumentations-Restrukturierung - Vorschlag + +## Aktuelle Situation + +### Statistiken +- **Gesamt:** 34 Markdown-Dateien im `deployment/` Ordner +- **Root-Level:** 21 Markdown-Dateien direkt in `deployment/` +- **Verlinkungen:** 35 Referenzen im `DOCUMENTATION_INDEX.md` + +### Problem +- Viele Dokumentationsdateien direkt im Root (`deployment/*.md`) +- Mischung aus: + - Haupt-Dokumentation (README.md, SETUP-GUIDE.md) + - Status/Tracking (DEPLOYMENT_SUMMARY.md, DEPLOYMENT-TODO.md) + - Test/Entwicklung (TEST_*.md, GIT_*.md, QUICK_TEST.md) + - Troubleshooting (WORKFLOW-TROUBLESHOOTING.md, CI_CD_STATUS.md) + - Logs/Historie (CLEANUP_LOG.md, CLEANUP_SUMMARY.md) +- Schwer ?berschaubar +- Konfigurationsdateien (ansible/, stacks/, gitea-runner/) vermischt mit Docs + +## Vorschlag: Strukturierte Dokumentation + +### Neue Struktur + +``` +deployment/ +??? README.md # ? Haupt-Dokumentation (bleibt) +??? docs/ # ?? Alle Dokumentation +? ??? README.md # Dokumentations-Index (ersetzt DOCUMENTATION_INDEX.md) +? ? +? ??? guides/ # ?? Anleitungen & Guides +? ? ??? setup-guide.md # (SETUP-GUIDE.md) +? ? ??? quick-start.md # (QUICK_START.md) +? ? ??? code-change-workflow.md # (CODE_CHANGE_WORKFLOW.md) +? ? ??? deployment-commands.md # (DEPLOYMENT_COMMANDS.md) +? ? ??? vault-password.md # (ansible/VAULT_PASSWORD.md) +? ? +? ??? reference/ # ?? Referenz-Dokumentation +? ? ??? application-stack.md # (APPLICATION_STACK_DEPLOYMENT.md) +? ? ??? ansible-playbooks.md # (ansible/README.md) +? ? ??? workflow-troubleshooting.md # (WORKFLOW-TROUBLESHOOTING.md) +? ? +? ??? status/ # ?? Status & Tracking +? ? ??? deployment-summary.md # (DEPLOYMENT_SUMMARY.md) +? ? ??? deployment-todo.md # (DEPLOYMENT-TODO.md) +? ? ??? ci-cd-status.md # (CI_CD_STATUS.md) +? ? ??? improvements.md # (IMPROVEMENTS.md) +? ? ??? next-steps.md # (NEXT_STEPS.md) +? ? +? ??? testing/ # ?? Test-Dokumentation +? ? ??? test-results.md # (TEST_RESULT.md) +? ? ??? git-deployment-test.md # (GIT_DEPLOYMENT_TEST.md) +? ? ??? git-deployment-issue.md # (GIT_DEPLOYMENT_ISSUE.md) +? ? ??? test-git-deployment.md # (TEST_GIT_DEPLOYMENT.md) +? ? ??? quick-test.md # (QUICK_TEST.md) +? ? ??? recommended-test-flow.md # (RECOMMENDED_TEST_FLOW.md) +? ? +? ??? history/ # ?? Logs & Historie +? ??? cleanup-log.md # (CLEANUP_LOG.md) +? ??? cleanup-summary.md # (CLEANUP_SUMMARY.md) +? +??? ansible/ # Ansible Konfiguration +? ??? README.md # ? Kurze ?bersicht, Link zu docs/reference/ +? ??? ... +? +??? stacks/ # Docker Compose Stacks +? ??? */README.md # Stack-spezifische Docs (bleiben) +? +??? gitea-runner/ # Runner Konfiguration + ??? README.md # Runner-spezifische Docs (bleibt) +``` + +## Vorteile + +### ? ?bersichtlichkeit +- Klare Trennung: Docs vs. Konfiguration +- Kategorisierung nach Zweck (Guides, Reference, Status, Tests, History) +- Einfacher zu navigieren + +### ? Wartbarkeit +- Logische Gruppierung ?hnlicher Dokumente +- Einfaches Finden relevanter Dokumentation +- Einfachere Aktualisierung + +### ? Klare Hierarchie +- `docs/README.md` als zentraler Einstiegspunkt +- `deployment/README.md` bleibt f?r Architektur-?bersicht +- Stack-spezifische READMEs bleiben nahe am Code + +### ? Saubere Struktur +- Konfigurationsdateien nicht mit Docs vermischt +- Tests getrennt von Produktions-Docs +- Historie/Logs in separatem Bereich + +## Nachteile + +### ?? Breaking Changes +- Alle Verlinkungen m?ssen aktualisiert werden +- Externe Referenzen (z.B. in anderen Projekten) m?ssen angepasst werden +- Git-Historie wird aufgeteilt + +### ?? Migration-Aufwand +- 21 Dateien m?ssen verschoben werden +- Verlinkungen in vielen Dateien aktualisieren +- CI/CD Skripte k?nnten Pfade referenzieren + +### ?? Gew?hnung +- Team muss neue Struktur lernen +- Neue Pfade m?ssen bekannt gemacht werden + +## Alternative: Minimale Restrukturierung + +Falls die vollst?ndige Restrukturierung zu aufw?ndig ist: + +### Option B: Nur Root-Level aufr?umen + +``` +deployment/ +??? README.md # Haupt-Dokumentation +??? DOCUMENTATION_INDEX.md # Index (bleibt) +? +??? docs/ # Nur Root-Level Docs verschieben +? ??? guides/ +? ? ??? setup-guide.md +? ? ??? quick-start.md +? ? ??? code-change-workflow.md +? ? +? ??? status/ +? ? ??? deployment-summary.md +? ? ??? deployment-todo.md +? ? ??? ci-cd-status.md +? ? +? ??? tests/ # Test-Dokumentation +? ??? test-results.md +? ??? ... +? +??? ansible/ # Bleibt wie es ist +? ??? README.md +? +??? stacks/ # Bleibt wie es ist +``` + +**Vorteile:** +- Weniger Breaking Changes +- Schneller umzusetzen +- Hauptproblem (Root-Level Unordnung) gel?st + +**Nachteile:** +- Keine vollst?ndige Strukturierung +- Stack-Docs bleiben verstreut + +## Empfehlung + +### F?r jetzt: **Option B (Minimal)** +1. ? Schnell umsetzbar +2. ? L?sen des Hauptproblems (21 Root-Level Dateien) +3. ? Minimal invasive ?nderungen +4. ? Stack-Docs bleiben nahe am Code (gut!) + +### Sp?ter: **Option A (Vollst?ndig)** wenn: +- Mehr Zeit f?r Migration vorhanden +- Automatisierung f?r Link-Updates verf?gbar +- Team w?chst und bessere Struktur ben?tigt + +## Migration-Plan (Option B) + +### Phase 1: Docs-Ordner erstellen +```bash +mkdir -p deployment/docs/{guides,status,tests} +``` + +### Phase 2: Dateien verschieben +```bash +# Guides +mv SETUP-GUIDE.md docs/guides/setup-guide.md +mv QUICK_START.md docs/guides/quick-start.md +mv CODE_CHANGE_WORKFLOW.md docs/guides/code-change-workflow.md +mv DEPLOYMENT_COMMANDS.md docs/guides/deployment-commands.md +mv ansible/VAULT_PASSWORD.md docs/guides/vault-password.md + +# Status +mv DEPLOYMENT_SUMMARY.md docs/status/deployment-summary.md +mv DEPLOYMENT-TODO.md docs/status/deployment-todo.md +mv CI_CD_STATUS.md docs/status/ci-cd-status.md +mv IMPROVEMENTS.md docs/status/improvements.md +mv NEXT_STEPS.md docs/status/next-steps.md + +# Tests +mv TEST_RESULT.md docs/tests/test-results.md +mv GIT_DEPLOYMENT_TEST.md docs/tests/git-deployment-test.md +mv GIT_DEPLOYMENT_ISSUE.md docs/tests/git-deployment-issue.md +mv TEST_GIT_DEPLOYMENT.md docs/tests/test-git-deployment.md +mv QUICK_TEST.md docs/tests/quick-test.md +mv RECOMMENDED_TEST_FLOW.md docs/tests/recommended-test-flow.md + +# Reference (optional, kann auch bleiben) +mv APPLICATION_STACK_DEPLOYMENT.md docs/reference/application-stack.md +mv WORKFLOW-TROUBLESHOOTING.md docs/reference/workflow-troubleshooting.md + +# History (optional) +mv CLEANUP_LOG.md docs/history/cleanup-log.md +mv CLEANUP_SUMMARY.md docs/history/cleanup-summary.md +``` + +### Phase 3: Verlinkungen aktualisieren +- `DOCUMENTATION_INDEX.md` aktualisieren +- `README.md` Verlinkungen pr?fen +- Interne Links in Dokumenten pr?fen +- CI/CD Skripte pr?fen + +### Phase 4: README.md erstellen +- `docs/README.md` als neuen Index erstellen +- `DOCUMENTATION_INDEX.md` ? `docs/README.md` migrieren + +## Entscheidung ben?tigt + +**Frage:** Soll die Dokumentation restrukturiert werden? + +- ? **Ja, Option B (Minimal)** - Nur Root-Level aufr?umen +- ? **Ja, Option A (Vollst?ndig)** - Komplette Restrukturierung +- ? **Nein** - Behalten wie es ist + +**Empfehlung:** Option B f?r jetzt, Option A sp?ter wenn n?tig. diff --git a/deployment/CODE_CHANGE_WORKFLOW.md b/deployment/docs/guides/code-change-workflow.md similarity index 98% rename from deployment/CODE_CHANGE_WORKFLOW.md rename to deployment/docs/guides/code-change-workflow.md index 8fb4ab62..1731f235 100644 --- a/deployment/CODE_CHANGE_WORKFLOW.md +++ b/deployment/docs/guides/code-change-workflow.md @@ -13,8 +13,8 @@ git push origin main → Automatisches Deployment startet (~8-15 Minuten) **📖 Verwandte Dokumentation:** -- **[Application Stack Deployment](APPLICATION_STACK_DEPLOYMENT.md)** - Wie das Deployment genau funktioniert -- **[CI/CD Status](CI_CD_STATUS.md)** - Aktueller Status der Pipeline +- **[Application Stack Deployment](../reference/application-stack.md)** - Wie das Deployment genau funktioniert +- **[CI/CD Status](../status/ci-cd-status.md)** - Aktueller Status der Pipeline --- diff --git a/deployment/DEPLOYMENT_COMMANDS.md b/deployment/docs/guides/deployment-commands.md similarity index 94% rename from deployment/DEPLOYMENT_COMMANDS.md rename to deployment/docs/guides/deployment-commands.md index cf1b54ca..342b6b26 100644 --- a/deployment/DEPLOYMENT_COMMANDS.md +++ b/deployment/docs/guides/deployment-commands.md @@ -116,9 +116,9 @@ ansible-playbook -i inventory/production.yml \ ## 📖 Vollständige Dokumentation -- **[README.md](README.md)** - Haupt-Dokumentation -- **[QUICK_START.md](QUICK_START.md)** - Schnellstart-Guide -- **[CODE_CHANGE_WORKFLOW.md](CODE_CHANGE_WORKFLOW.md)** - Codeänderungen workflow +- **[README.md](../../README.md)** - Haupt-Dokumentation +- **[quick-start.md](quick-start.md)** - Schnellstart-Guide +- **[code-change-workflow.md](code-change-workflow.md)** - Codeänderungen workflow --- diff --git a/deployment/QUICK_START.md b/deployment/docs/guides/quick-start.md similarity index 98% rename from deployment/QUICK_START.md rename to deployment/docs/guides/quick-start.md index 3e81fa63..5e9f107c 100644 --- a/deployment/QUICK_START.md +++ b/deployment/docs/guides/quick-start.md @@ -88,7 +88,7 @@ curl https://michaelschiemer.de/health ### Setup-Dokumentation -- **`SETUP-GUIDE.md`** - Kompletter Setup-Guide +- **`docs/guides/setup-guide.md`** - Kompletter Setup-Guide - **`ansible/README.md`** - Ansible Playbooks Dokumentation - **`stacks/application/README.md`** - Application Stack Details diff --git a/deployment/SETUP-GUIDE.md b/deployment/docs/guides/setup-guide.md similarity index 100% rename from deployment/SETUP-GUIDE.md rename to deployment/docs/guides/setup-guide.md diff --git a/deployment/docs/guides/vault-password.md b/deployment/docs/guides/vault-password.md new file mode 100644 index 00000000..eb3aea4c --- /dev/null +++ b/deployment/docs/guides/vault-password.md @@ -0,0 +1,262 @@ +# Ansible Vault Password Dokumentation + +## ?bersicht + +Das Ansible Vault-Passwort wird verwendet, um verschl?sselte Secrets-Dateien (`production.vault.yml`) zu sch?tzen. Diese Dokumentation beschreibt, wie das Vault-Passwort angelegt, gespeichert und verwendet wird. + +## Historischer Kontext + +### Erstellungsdatum +- **Erstellt am:** 30. Oktober 2025, 21:42:27 +- **Datei:** `deployment/ansible/secrets/.vault_pass` +- **Erstes Setup-Script:** 31. Oktober 2025 (Commit `e26eb2a`) +- **Script-Datei:** `deployment/ansible/scripts/init-secrets.sh` + +### Einf?hrung +Das Vault-Passwort-System wurde im Rahmen des CI/CD-Pipeline-Setups eingef?hrt, um die sichere Verwaltung von Production-Secrets zu erm?glichen. Das automatische Setup-Script wurde am 31. Oktober 2025 hinzugef?gt, um die manuelle Erstellung zu vereinfachen. + +## Speicherort und Sicherheit + +### Dateispeicherort +- **Pfad:** `deployment/ansible/secrets/.vault_pass` +- **Berechtigungen:** `600` (nur Owner lesbar/schreibbar) +- **Gitignored:** ? Ja (in `.gitignore` hinterlegt) +- **Inhalt:** Eine Zeile mit dem Vault-Passwort (plaintext) + +### Sicherheitshinweise +?? **WICHTIG:** +- Das Passwort ist in Klartext in der Datei gespeichert +- Die Datei ist **gitignored** und wird **nie** ins Repository committet +- Berechtigungen sind auf `600` gesetzt (nur Owner-Zugriff) +- Das Passwort sollte zus?tzlich im **Passwort-Manager** gespeichert werden +- F?r verschiedene Umgebungen (dev/staging/prod) sollten unterschiedliche Passw?rter verwendet werden + +## Erstellung des Vault-Passworts + +### Methode 1: Automatisiertes Script (Empfohlen) + +Das Script `init-secrets.sh` f?hrt Sie interaktiv durch den Setup-Prozess: + +```bash +cd deployment/ansible +./scripts/init-secrets.sh +``` + +**Was das Script macht:** +1. Pr?ft, ob `.vault_pass` bereits existiert +2. Falls nicht vorhanden: Fragt interaktiv nach dem Passwort (mit Best?tigung) +3. Speichert das Passwort in `secrets/.vault_pass` +4. Setzt Berechtigungen auf `600` +5. Erstellt und verschl?sselt `production.vault.yml` (optional) + +**Vorteile:** +- Automatische Berechtigungen +- Passwort-Best?tigung verhindert Tippfehler +- Vollst?ndiger Setup-Workflow inkl. Vault-Datei-Erstellung + +### Methode 2: Manuelle Erstellung + +```bash +cd deployment/ansible/secrets + +# Passwort erstellen +echo "your-secure-vault-password-here" > .vault_pass + +# Sicherheit: Berechtigungen setzen +chmod 600 .vault_pass +``` + +**Wichtig:** Verwenden Sie ein sicheres, zuf?lliges Passwort! + +## Verwendung des Vault-Passworts + +### In Ansible Playbooks + +Das Vault-Passwort wird bei der Ausf?hrung von Playbooks ?bergeben: + +```bash +# Beispiel: Production Secrets deployen +ansible-playbook playbooks/setup-production-secrets.yml \ + --vault-password-file secrets/.vault_pass + +# Beispiel: Infrastructure deployen +ansible-playbook playbooks/setup-infrastructure.yml \ + -i inventory/production.yml \ + --vault-password-file secrets/.vault_pass + +# Beispiel: Application Update deployen +ansible-playbook playbooks/deploy-update.yml \ + -e "image_tag=sha-abc123" \ + --vault-password-file secrets/.vault_pass +``` + +### Vault-Dateien verwalten + +```bash +# Vault-Datei entschl?sseln und anzeigen +ansible-vault view secrets/production.vault.yml \ + --vault-password-file secrets/.vault_pass + +# Vault-Datei bearbeiten +ansible-vault edit secrets/production.vault.yml \ + --vault-password-file secrets/.vault_pass + +# Neue Vault-Datei erstellen und verschl?sseln +ansible-vault encrypt secrets/production.vault.yml \ + --vault-password-file secrets/.vault_pass + +# Vault-Datei entschl?sseln (dauerhaft) +ansible-vault decrypt secrets/production.vault.yml \ + --vault-password-file secrets/.vault_pass +``` + +## CI/CD Integration + +### Gitea Actions + +In Gitea Actions wird das Vault-Passwort als Secret gespeichert: + +- **Secret-Name:** `ANSIBLE_VAULT_PASSWORD` +- **Verwendung:** Wird automatisch in Workflows verwendet, die Ansible-Vault ben?tigen + +**Hinzuf?gen des Secrets in Gitea:** +1. Gehe zu: Repository Settings ? Secrets +2. Erstelle neues Secret: `ANSIBLE_VAULT_PASSWORD` +3. Wert: Das Vault-Passwort aus `.vault_pass` + +**Workflow-Beispiel:** +```yaml +- name: Deploy with Ansible + run: | + ansible-playbook playbooks/deploy-update.yml \ + --vault-password-file <(echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}") +``` + +## Passwort zur?cksetzen/?ndern + +### Passwort ?ndern + +Wenn das Vault-Passwort ge?ndert werden muss: + +```bash +cd deployment/ansible + +# 1. Alte Vault-Datei entschl?sseln (mit altem Passwort) +ansible-vault decrypt secrets/production.vault.yml \ + --vault-password-file secrets/.vault_pass + +# 2. Neues Passwort setzen +echo "new-secure-vault-password" > secrets/.vault_pass +chmod 600 secrets/.vault_pass + +# 3. Vault-Datei mit neuem Passwort verschl?sseln +ansible-vault encrypt secrets/production.vault.yml \ + --vault-password-file secrets/.vault_pass + +# 4. Passwort im Passwort-Manager aktualisieren +# 5. CI/CD Secret in Gitea aktualisieren (ANSIBLE_VAULT_PASSWORD) +``` + +### Passwort vergessen + +?? **Wenn das Vault-Passwort verloren geht:** +- Die verschl?sselte `production.vault.yml` kann nicht mehr entschl?sselt werden +- Eine neue Vault-Datei muss erstellt werden +- Alle Secrets m?ssen neu konfiguriert werden +- **L?sung:** Passwort im Passwort-Manager speichern! + +## Troubleshooting + +### Problem: "Decryption failed" + +**Fehler:** +``` +ERROR! Decryption failed (no vault secrets were found) +``` + +**L?sung:** +1. Passwort-Datei pr?fen: + ```bash + cat deployment/ansible/secrets/.vault_pass + ``` +2. Korrekten Pfad verwenden: + ```bash + --vault-password-file secrets/.vault_pass + ``` +3. Berechtigungen pr?fen: + ```bash + ls -la deployment/ansible/secrets/.vault_pass + # Sollte: -rw------- (600) + ``` + +### Problem: "Vault password file not found" + +**Fehler:** +``` +ERROR! the vault password file secrets/.vault_pass was not found +``` + +**L?sung:** +```bash +# Pr?fen ob Datei existiert +ls -la deployment/ansible/secrets/.vault_pass + +# Falls nicht vorhanden, neu erstellen (siehe "Erstellung des Vault-Passworts") +``` + +### Problem: "Permission denied" + +**Fehler:** +``` +Permission denied: secrets/.vault_pass +``` + +**L?sung:** +```bash +chmod 600 deployment/ansible/secrets/.vault_pass +``` + +## Best Practices + +### ? Empfohlene Vorgehensweise + +1. **Passwort-Manager:** Vault-Passwort im Passwort-Manager speichern +2. **Sichere Passw?rter:** Verwendung von zuf?lligen, starken Passw?rtern +3. **Separate Passw?rter:** Verschiedene Passw?rter f?r dev/staging/prod +4. **Regelm??ige Rotation:** Passwort regelm??ig ?ndern (z.B. viertelj?hrlich) +5. **Backup:** Passwort an sicherem Ort (Passwort-Manager) sichern +6. **Zugriffskontrolle:** Nur autorisierte Personen sollten Zugriff haben + +### ? Zu vermeiden + +- Passwort ins Repository committen (gitignored!) +- Passwort in unverschl?sselten Dokumenten speichern +- Passwort per Email oder Chat teilen +- Einfache/erratbare Passw?rter verwenden +- Passwort mehreren Umgebungen teilen + +## Verwandte Dateien + +- **Setup-Script:** [`deployment/ansible/scripts/init-secrets.sh`](../../ansible/scripts/init-secrets.sh) +- **Vault-Datei:** `deployment/ansible/secrets/production.vault.yml` +- **Vault-Template:** `deployment/ansible/secrets/production.vault.yml.example` +- **Gitignore:** `deployment/ansible/secrets/.gitignore` +- **Haupt-Dokumentation:** [`deployment/ansible/README.md`](../../ansible/README.md) +- **Setup-Guide:** [`deployment/docs/guides/setup-guide.md`](setup-guide.md) + +## Referenzen + +- [Ansible Vault Dokumentation](https://docs.ansible.com/ansible/latest/user_guide/vault.html) +- [Ansible Vault Best Practices](https://docs.ansible.com/ansible/latest/user_guide/vault.html#best-practices) + +## Zusammenfassung + +| Aspekt | Details | +|--------|---------| +| **Erstellt am** | 30. Oktober 2025, 21:42:27 | +| **Speicherort** | `deployment/ansible/secrets/.vault_pass` | +| **Berechtigungen** | `600` (nur Owner) | +| **Gitignored** | ? Ja | +| **Setup-Script** | `scripts/init-secrets.sh` | +| **CI/CD Secret** | `ANSIBLE_VAULT_PASSWORD` | +| **Verwendung** | `--vault-password-file secrets/.vault_pass` | diff --git a/deployment/CLEANUP_LOG.md b/deployment/docs/history/cleanup-log.md similarity index 100% rename from deployment/CLEANUP_LOG.md rename to deployment/docs/history/cleanup-log.md diff --git a/deployment/CLEANUP_SUMMARY.md b/deployment/docs/history/cleanup-summary.md similarity index 100% rename from deployment/CLEANUP_SUMMARY.md rename to deployment/docs/history/cleanup-summary.md diff --git a/deployment/APPLICATION_STACK_DEPLOYMENT.md b/deployment/docs/reference/application-stack.md similarity index 98% rename from deployment/APPLICATION_STACK_DEPLOYMENT.md rename to deployment/docs/reference/application-stack.md index afde4d84..04ee6e15 100644 --- a/deployment/APPLICATION_STACK_DEPLOYMENT.md +++ b/deployment/docs/reference/application-stack.md @@ -5,8 +5,8 @@ Dieses Dokument erklärt, wie genau das Deployment in den Application Stack abläuft, wenn die CI/CD Pipeline ausgeführt wird. **📖 Verwandte Dokumentation:** -- **[Code Changes Workflow](CODE_CHANGE_WORKFLOW.md)** - Wie Codeänderungen gepusht werden -- **[CI/CD Status](CI_CD_STATUS.md)** - Aktueller Status der Pipeline +- **[Code Changes Workflow](../guides/code-change-workflow.md)** - Wie Codeänderungen gepusht werden +- **[CI/CD Status](../status/ci-cd-status.md)** - Aktueller Status der Pipeline --- diff --git a/deployment/WORKFLOW-TROUBLESHOOTING.md b/deployment/docs/reference/workflow-troubleshooting.md similarity index 100% rename from deployment/WORKFLOW-TROUBLESHOOTING.md rename to deployment/docs/reference/workflow-troubleshooting.md diff --git a/deployment/CI_CD_STATUS.md b/deployment/docs/status/ci-cd-status.md similarity index 96% rename from deployment/CI_CD_STATUS.md rename to deployment/docs/status/ci-cd-status.md index 98c2d7df..987f1461 100644 --- a/deployment/CI_CD_STATUS.md +++ b/deployment/docs/status/ci-cd-status.md @@ -4,8 +4,8 @@ ## 📖 Dokumentation -- **[Code Changes Workflow](CODE_CHANGE_WORKFLOW.md)** - Anleitung: Wie Codeänderungen gepusht und deployed werden -- **[Application Stack Deployment](APPLICATION_STACK_DEPLOYMENT.md)** - Detaillierte Erklärung des Deployment-Prozesses +- **[Code Changes Workflow](../guides/code-change-workflow.md)** - Anleitung: Wie Codeänderungen gepusht und deployed werden +- **[Application Stack Deployment](../reference/application-stack.md)** - Detaillierte Erklärung des Deployment-Prozesses ## ✅ Was bereits vorhanden ist diff --git a/deployment/DEPLOYMENT_SUMMARY.md b/deployment/docs/status/deployment-summary.md similarity index 100% rename from deployment/DEPLOYMENT_SUMMARY.md rename to deployment/docs/status/deployment-summary.md diff --git a/deployment/DEPLOYMENT-TODO.md b/deployment/docs/status/deployment-todo.md similarity index 100% rename from deployment/DEPLOYMENT-TODO.md rename to deployment/docs/status/deployment-todo.md diff --git a/deployment/IMPROVEMENTS.md b/deployment/docs/status/improvements.md similarity index 100% rename from deployment/IMPROVEMENTS.md rename to deployment/docs/status/improvements.md diff --git a/deployment/NEXT_STEPS.md b/deployment/docs/status/next-steps.md similarity index 100% rename from deployment/NEXT_STEPS.md rename to deployment/docs/status/next-steps.md diff --git a/deployment/GIT_DEPLOYMENT_ISSUE.md b/deployment/docs/tests/git-deployment-issue.md similarity index 100% rename from deployment/GIT_DEPLOYMENT_ISSUE.md rename to deployment/docs/tests/git-deployment-issue.md diff --git a/deployment/GIT_DEPLOYMENT_TEST.md b/deployment/docs/tests/git-deployment-test.md similarity index 100% rename from deployment/GIT_DEPLOYMENT_TEST.md rename to deployment/docs/tests/git-deployment-test.md diff --git a/deployment/QUICK_TEST.md b/deployment/docs/tests/quick-test.md similarity index 100% rename from deployment/QUICK_TEST.md rename to deployment/docs/tests/quick-test.md diff --git a/deployment/RECOMMENDED_TEST_FLOW.md b/deployment/docs/tests/recommended-test-flow.md similarity index 100% rename from deployment/RECOMMENDED_TEST_FLOW.md rename to deployment/docs/tests/recommended-test-flow.md diff --git a/deployment/TEST_GIT_DEPLOYMENT.md b/deployment/docs/tests/test-git-deployment.md similarity index 100% rename from deployment/TEST_GIT_DEPLOYMENT.md rename to deployment/docs/tests/test-git-deployment.md diff --git a/deployment/TEST_RESULT.md b/deployment/docs/tests/test-results.md similarity index 100% rename from deployment/TEST_RESULT.md rename to deployment/docs/tests/test-results.md diff --git a/deployment/stacks/staging/docker-compose.yml b/deployment/stacks/staging/docker-compose.yml index 02571865..51aeabb7 100644 --- a/deployment/stacks/staging/docker-compose.yml +++ b/deployment/stacks/staging/docker-compose.yml @@ -176,6 +176,12 @@ services: find /var/www/html.orig -mindepth 1 -maxdepth 1 ! -name "storage" -exec cp -r {} "$$GIT_TARGET_DIR/" \; 2>/dev/null || true fi + # Fix nginx upstream configuration - sites-enabled/default overrides conf.d/default.conf + if [ -f "/etc/nginx/sites-available/default" ]; then + echo "🔧 [staging-nginx] Fixing PHP-FPM upstream configuration..." + sed -i "s|server 127.0.0.1:9000;|server staging-app:9000;|g" /etc/nginx/sites-available/default || true + fi + # Start nginx only (no PHP-FPM, no Git clone - staging-app container handles that) echo "🚀 [staging-nginx] Starting nginx..." exec nginx -g "daemon off;" diff --git a/docs/Makefile.md b/docs/Makefile.md new file mode 100644 index 00000000..b3121b01 --- /dev/null +++ b/docs/Makefile.md @@ -0,0 +1,66 @@ +# Makefile Quick Reference + +Dieses Dokument fasst die wichtigsten Ziele aus dem Projekt-Makefile zusammen und erklärt, wie du sie anpasst. + +## Grundlagen + +- `make help` listet alle verfügbaren Targets mit Kurzbeschreibung. +- Alle Docker-Befehle nutzen standardmäßig `docker compose`; überschreibe das Kommando bei Bedarf mit `DOCKER=...`. +- Variablen wie `PHP_SERVICE`, `COMPOSER_BIN`, `NODE_BIN` oder `ARGS` lassen sich beim Aufruf anpassen, z. B. `make phpstan PHP_SERVICE=php-alt`. + +## Container-Steuerung + +| Befehl | Beschreibung | +| ------ | ------------ | +| `make up` | startet die lokale Docker-Umgebung im Hintergrund | +| `make down` | stoppt und entfernt Container, Netzwerke und Volumes | +| `make build` | baut die Images neu | +| `make restart` | startet den PHP-Container neu (`PHP_SERVICE`) | +| `make logs SERVICE=name` | streamt Logs (Standard: alle Container) | +| `make ps` | zeigt laufende Container | + +## Composer & Anwendung + +| Befehl | Beschreibung | +| ------ | ------------ | +| `make composer-install` | führt `composer install` im PHP-Container aus | +| `make composer-update` | führt `composer update` aus | +| `make autoload` | aktualisiert den Autoloader (`composer dump-autoload -o`) | +| `make console CMD="cache:clear"` | ruft `php console.php` mit übergebenem Befehl auf | +| `make cache-clear` | ruft intern `console` mit `cache:clear` auf | + +## Tests & Qualitätssicherung + +| Befehl | Beschreibung | +| ------ | ------------ | +| `make test` | führt alle Pest-Tests im Profil `test` aus; weitere Parameter via `ARGS` | +| `make test-unit` / `test-domain` / `test-framework` | scoped Pest-Läufe für einzelne Ordner | +| `make test-watch` | Pest im Watch-Modus | +| `make test-coverage` / `test-coverage-html` | Coverage-Reports (Text bzw. HTML) | +| `make test-filter FILTER=Klasse` | filtert Tests nach Name | +| `make phpstan` | PHPStan-Analyse im Container | +| `make phpstan-baseline` | erzeugt eine neue Baseline | +| `make cs` | php-cs-fixer Dry-Run | +| `make cs-fix` | php-cs-fixer mit automatischer Korrektur | +| `make security-check` | führt `composer audit` aus | + +## Frontend-Helper + +| Befehl | Beschreibung | +| ------ | ------------ | +| `make npm-install` | installiert Node-Abhängigkeiten | +| `make npm-build` | baut Produktions-Assets via Vite | +| `make npm-dev` | startet den Vite-Dev-Server | +| `make npm-test` | führt JavaScript-Tests aus | + +## Git & Deployment + +| Befehl | Beschreibung | +| ------ | ------------ | +| `make push-staging` | pusht den aktuellen HEAD nach `origin/staging` | + +## Tipps + +- `console` und andere Container-Kommandos verwenden `docker exec -it`; wenn die Umgebung keine TTY mag (z. B. CI), `TTY=0 make console CMD="list"` ausführen und das Makefile bei Bedarf erweitern. +- Tests laufen im Container `php-test` mit dem Compose-Profil `test`. Vor der Nutzung sicherstellen, dass das Profil gebaut wurde (`make build`). +- Zusätzliche Shortcuts lassen sich leicht durch neue Targets ergänzen – einfach mit Beschreibung versehen und in `.PHONY` aufnehmen, damit GNU Make nicht versucht, gleichnamige Dateien zu bauen. diff --git a/scripts/delete-all-workflow-runs.sh b/scripts/delete-all-workflow-runs.sh new file mode 100755 index 00000000..017dd108 --- /dev/null +++ b/scripts/delete-all-workflow-runs.sh @@ -0,0 +1,169 @@ +#!/bin/bash +# Script to delete all workflow runs from Gitea repository +# Usage: ./scripts/delete-all-workflow-runs.sh [GITEA_TOKEN] + +set -euo pipefail + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +GITEA_URL="${GITEA_URL:-https://git.michaelschiemer.de}" +REPO_OWNER="${REPO_OWNER:-michael}" +REPO_NAME="${REPO_NAME:-michaelschiemer}" + +# Try to get token from parameter, env var, CI_TOKEN, or Ansible vault +if [ -n "${1:-}" ]; then + GITEA_TOKEN="$1" +elif [ -n "${GITEA_TOKEN:-}" ]; then + # Token already set + : +elif [ -n "${CI_TOKEN:-}" ]; then + GITEA_TOKEN="$CI_TOKEN" +else + # Try to extract from Ansible vault + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + VAULT_FILE="$PROJECT_ROOT/deployment/ansible/secrets/production.vault.yml" + VAULT_PASS="$PROJECT_ROOT/deployment/ansible/.vault_pass" + + if [ -f "$VAULT_FILE" ] && command -v ansible-playbook >/dev/null 2>&1; then + echo -e "${BLUE}Trying to extract CI_TOKEN from Ansible vault...${NC}" + if [ -f "$VAULT_PASS" ]; then + TOKEN=$(cd "$PROJECT_ROOT" && ansible localhost -m debug -a "var=vault_git_token" -e "@deployment/ansible/secrets/production.vault.yml" --vault-password-file "$VAULT_PASS" 2>/dev/null | grep -oP "vault_git_token.*\"\K[^\"]+" | head -1 || echo "") + else + TOKEN=$(cd "$PROJECT_ROOT" && ansible localhost -m debug -a "var=vault_git_token" -e "@deployment/ansible/secrets/production.vault.yml" --ask-vault-pass <<< "" 2>/dev/null | grep -oP "vault_git_token.*\"\K[^\"]+" | head -1 || echo "") + fi + + if [ -n "$TOKEN" ] && [ "$TOKEN" != "null" ] && [ "$TOKEN" != "undefined" ]; then + GITEA_TOKEN="$TOKEN" + echo -e "${GREEN}? Token extracted from Ansible vault${NC}" + fi + fi +fi + +echo -e "${BLUE}=== Gitea Workflow Runs Deletion ===${NC}" +echo "" + +# Check if token is provided +if [ -z "$GITEA_TOKEN" ]; then + echo -e "${YELLOW}?? GITEA_TOKEN nicht gesetzt${NC}" + echo "" + echo "Bitte generiere einen Gitea Access Token:" + echo "1. Gehe zu: ${GITEA_URL}/user/settings/applications" + echo "2. Klicke 'Generate New Token'" + echo "3. Name: z.B. 'delete-workflow-runs'" + echo "4. Scopes: 'write:repository' (mindestens)" + echo "5. Kopiere den Token" + echo "" + echo "Dann f?hre aus:" + echo " export GITEA_TOKEN='dein-token'" + echo " ./scripts/delete-all-workflow-runs.sh" + echo "" + exit 1 +fi + +# Function to get all workflow runs +get_workflow_runs() { + local page="${1:-1}" + local per_page="${2:-100}" + + curl -s \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Accept: application/json" \ + "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/actions/runs?page=${page}&limit=${per_page}" +} + +# Function to delete a workflow run +delete_workflow_run() { + local run_id=$1 + + local response=$(curl -s -w "\n%{http_code}" \ + -X DELETE \ + -H "Authorization: token ${GITEA_TOKEN}" \ + "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/actions/runs/${run_id}") + + local http_code=$(echo "$response" | tail -n1) + local body=$(echo "$response" | sed '$d') + + if [ "$http_code" = "204" ] || [ "$http_code" = "200" ]; then + return 0 + else + echo -e "${RED}? FAILED (HTTP $http_code)${NC}" + echo "Response: $body" + return 1 + fi +} + +echo "Repository: ${REPO_OWNER}/${REPO_NAME}" +echo "Gitea URL: ${GITEA_URL}" +echo "" + +# Fetch all workflow runs +echo -e "${BLUE}Fetching workflow runs...${NC}" +all_runs="" +page=1 +total_deleted=0 + +while true; do + echo -n "Loading page ${page}... " + runs_data=$(get_workflow_runs "$page" 100) + + # Check if we got any runs + if ! echo "$runs_data" | grep -q "\"workflow_runs\""; then + echo "no more runs" + break + fi + + # Extract run IDs using jq if available, otherwise use grep/sed + if command -v jq >/dev/null 2>&1; then + run_ids=$(echo "$runs_data" | jq -r '.workflow_runs[]?.id // empty' 2>/dev/null || echo "") + total_count=$(echo "$runs_data" | jq -r '.total_count // 0' 2>/dev/null || echo "0") + else + # Fallback: extract IDs with grep/sed + run_ids=$(echo "$runs_data" | grep -o '"id":[0-9]*' | grep -o '[0-9]*' || echo "") + total_count=$(echo "$runs_data" | grep -o '"total_count":[0-9]*' | grep -o '[0-9]*' | head -1 || echo "0") + fi + + if [ -z "$run_ids" ]; then + echo "no runs found" + break + fi + + run_count=$(echo "$run_ids" | wc -l) + echo "${run_count} runs found" + + # Delete each run + for run_id in $run_ids; do + if [ -n "$run_id" ] && [ "$run_id" != "null" ]; then + echo -n " Deleting run ${run_id}... " + if delete_workflow_run "$run_id"; then + echo -e "${GREEN}?${NC}" + total_deleted=$((total_deleted + 1)) + else + echo -e "${RED}?${NC}" + fi + fi + done + + # Check if there are more pages + if command -v jq >/dev/null 2>&1; then + has_more=$(echo "$runs_data" | jq -r 'if .workflow_runs | length > 0 then true else false end' 2>/dev/null || echo "false") + else + has_more=$(echo "$runs_data" | grep -q '"workflow_runs"' && echo "true" || echo "false") + fi + + if [ "$has_more" != "true" ] || [ "$run_count" -eq 0 ]; then + break + fi + + page=$((page + 1)) +done + +echo "" +echo -e "${GREEN}=== Deletion Complete ===${NC}" +echo -e "Total runs deleted: ${total_deleted}" +echo "" diff --git a/scripts/extract-ci-token.yml b/scripts/extract-ci-token.yml new file mode 100644 index 00000000..3d99b170 --- /dev/null +++ b/scripts/extract-ci-token.yml @@ -0,0 +1,11 @@ +--- +- hosts: localhost + connection: local + gather_facts: no + vars_files: + - deployment/ansible/secrets/production.vault.yml + tasks: + - name: Extract CI_TOKEN + debug: + msg: "{{ vault_git_token }}" + no_log: false diff --git a/scripts/get-ci-token-from-vault.sh b/scripts/get-ci-token-from-vault.sh new file mode 100755 index 00000000..50e7331b --- /dev/null +++ b/scripts/get-ci-token-from-vault.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Script to extract CI_TOKEN (vault_git_token) from Ansible Vault +# Usage: ./scripts/get-ci-token-from-vault.sh + +set -e + +VAULT_FILE="deployment/ansible/secrets/production.vault.yml" +VAULT_PASS_FILE="deployment/ansible/.vault_pass" + +# Check if vault file exists +if [ ! -f "$VAULT_FILE" ]; then + echo "Error: Vault file not found at $VAULT_FILE" + exit 1 +fi + +# Try to extract token +if [ -f "$VAULT_PASS_FILE" ]; then + # Use vault password file + TOKEN=$(ansible-vault view "$VAULT_FILE" --vault-password-file "$VAULT_PASS_FILE" 2>/dev/null | grep "vault_git_token:" | cut -d'"' -f2 || echo "") +elif command -v ansible-playbook >/dev/null 2>&1; then + # Try with ansible-playbook + TOKEN=$(cd deployment/ansible && ansible-playbook -i localhost, -c local /dev/stdin --vault-password-file .vault_pass 2>/dev/null < */ - $allowedEnvs = ['development', 'testing', 'production']; + /*$allowedEnvs = ['development', 'testing', 'production']; $appEnv = $this->env->getString(EnvKey::APP_ENV, 'production'); if (! in_array($appEnv, $allowedEnvs, true)) { $issues[] = [ @@ -37,7 +36,7 @@ final readonly class ConfigValidator 'severity' => 'medium', 'recommendation' => 'Setze APP_ENV auf einen der Werte: development | testing | production', ]; - } + }*/ // APP_DEBUG should be boolean-like if present if ($this->env->has(EnvKey::APP_DEBUG)) { diff --git a/src/Framework/Http/MiddlewareInvoker.php b/src/Framework/Http/MiddlewareInvoker.php index 4b81ecdf..e429a19c 100644 --- a/src/Framework/Http/MiddlewareInvoker.php +++ b/src/Framework/Http/MiddlewareInvoker.php @@ -11,11 +11,12 @@ use App\Framework\DI\Container; use App\Framework\Http\Exceptions\MiddlewareTimeoutException; use App\Framework\Http\Metrics\MiddlewareMetricsCollector; use App\Framework\Logging\DefaultLogger; +use App\Framework\Logging\Logger; use App\Framework\Logging\ValueObjects\LogContext; final readonly class MiddlewareInvoker { - private DefaultLogger $logger; + private Logger $logger; /** * Default timeout for middleware execution in seconds @@ -46,7 +47,7 @@ final readonly class MiddlewareInvoker ?MiddlewareCircuitBreaker $circuitBreaker = null, ?MiddlewareMetricsCollector $metricsCollector = null ) { - $this->logger = $this->container->get(DefaultLogger::class); + $this->logger = $this->container->get(Logger::class); if ($defaultTimeout === null) { try { @@ -80,13 +81,13 @@ final readonly class MiddlewareInvoker // Middleware-Instanz holen, falls ein Klassenname übergeben wurde if (is_string($middleware)) { - error_log("MiddlewareInvoker: Getting instance for {$middleware}"); + #error_log("MiddlewareInvoker: Getting instance for {$middleware}"); try { $middleware = $this->container->get($middleware); - error_log("MiddlewareInvoker: Successfully got instance for " . get_class($middleware)); + #error_log("MiddlewareInvoker: Successfully got instance for " . get_class($middleware)); } catch (\Throwable $e) { - error_log("MiddlewareInvoker: FAILED to get instance for {$middleware}: " . $e->getMessage()); + #error_log("MiddlewareInvoker: FAILED to get instance for {$middleware}: " . $e->getMessage()); throw $e; } @@ -108,8 +109,8 @@ final readonly class MiddlewareInvoker }*/ // Debug-Ausgabe vor der Ausführung - $this->logger->debug('VOR: Middleware ' . $middlewareName . - ' - hasResponse: ' . ($context->hasResponse() ? 'ja' : 'nein')); + #$this->logger->debug('VOR: Middleware ' . $middlewareName . + # ' - hasResponse: ' . ($context->hasResponse() ? 'ja' : 'nein')); try { // Start timing the middleware execution @@ -133,7 +134,7 @@ final readonly class MiddlewareInvoker } // Log execution time for monitoring - $this->logger->debug("Middleware {$middlewareName} executed in {$executionTime} seconds"); + #$this->logger->debug("Middleware {$middlewareName} executed in {$executionTime} seconds"); $response = $resultContext->response; @@ -161,8 +162,8 @@ final readonly class MiddlewareInvoker ); // Debug-Ausgabe nach der Ausführung - $this->logger->debug('NACH: Middleware ' . $middlewareName . - ' - hasResponse: ' . ($resultContext->hasResponse() ? 'ja' : 'nein')); + #$this->logger->debug('NACH: Middleware ' . $middlewareName . + # ' - hasResponse: ' . ($resultContext->hasResponse() ? 'ja' : 'nein')); return $resultContext; } catch (\Throwable $e) { diff --git a/src/Framework/Logging/LoggerInitializer.php b/src/Framework/Logging/LoggerInitializer.php index 4439dd56..7f7db8ce 100644 --- a/src/Framework/Logging/LoggerInitializer.php +++ b/src/Framework/Logging/LoggerInitializer.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace App\Framework\Logging; use App\Framework\Config\Environment; +use App\Framework\Config\EnvKey; use App\Framework\Config\TypedConfiguration; use App\Framework\Core\PathProvider; use App\Framework\DI\Container; @@ -69,7 +70,7 @@ final readonly class LoggerInitializer // Stelle sicher, dass alle Logverzeichnisse existieren $logConfig->ensureLogDirectoriesExist(); - $redisConfig = new RedisConfig(host: 'redis', database: 2); + $redisConfig = new RedisConfig(host: $env->getString(EnvKey::REDIS_HOST, 'redis'), database: 2); $redisConnection = new RedisConnection($redisConfig, 'queue'); $queue = new RedisQueue($redisConnection, 'commands');