fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled

This commit is contained in:
2025-11-24 21:28:25 +01:00
parent 4eb7134853
commit 77abc65cd7
1327 changed files with 91915 additions and 9909 deletions

View File

@@ -0,0 +1,48 @@
# Semaphore CI Stack - Environment Configuration
# Copy this file to .env and adjust values as needed
# ============================================
# MySQL Database Configuration
# ============================================
MYSQL_ROOT_PASSWORD=semaphore_root
MYSQL_DATABASE=semaphore
MYSQL_USER=semaphore
MYSQL_PASSWORD=semaphore
# ============================================
# Semaphore Configuration
# ============================================
# Port binding (default: 3000)
# Only accessible via localhost (127.0.0.1)
SEMAPHORE_PORT=3000
# Admin User Configuration
SEMAPHORE_ADMIN=admin
SEMAPHORE_ADMIN_NAME=Administrator
SEMAPHORE_ADMIN_EMAIL=admin@localhost
SEMAPHORE_ADMIN_PASSWORD=admin
# Playbook Storage Path (inside container)
SEMAPHORE_PLAYBOOK_PATH=/tmp/semaphore
# Access Key Encryption
# Generate with: head -c32 /dev/urandom | base64
# IMPORTANT: Change this in production!
SEMAPHORE_ACCESS_KEY_ENCRYPTION=change-me-in-production
# ============================================
# Optional: LDAP Configuration
# ============================================
# SEMAPHORE_LDAP_ENABLED=false
# SEMAPHORE_LDAP_HOST=ldap.example.com
# SEMAPHORE_LDAP_PORT=389
# SEMAPHORE_LDAP_DN=cn=admin,dc=example,dc=com
# SEMAPHORE_LDAP_PASSWORD=ldap_password
# SEMAPHORE_LDAP_BASE_DN=dc=example,dc=com
# SEMAPHORE_LDAP_USER_FILTER=(uid=%s)
# ============================================
# Optional: Webhook Configuration
# ============================================
# SEMAPHORE_WEBHOOK_URL=http://localhost:8080/webhook

View File

@@ -0,0 +1,201 @@
# ?? Quick Start: Semaphore mit Repository verbinden
Kurze Schritt-f?r-Schritt-Anleitung, um Semaphore mit deinem Git-Repository zu verbinden.
## Schritt 1: Semaphore starten
```bash
cd deployment/stacks/semaphore
cp env.example .env
```
**WICHTIG**: Generiere einen Encryption Key:
```bash
head -c32 /dev/urandom | base64
```
Kopiere den Key und setze ihn in `.env`:
```env
SEMAPHORE_ACCESS_KEY_ENCRYPTION=<dein-generierter-key>
```
```bash
docker compose up -d
```
## Schritt 2: Semaphore ?ffnen
?ffne im Browser: **http://localhost:9300**
**Login**:
- Username: `admin`
- Password: `admin`
## Schritt 3: Projekt erstellen
1. Klicke auf **"New Project"** oder **"Create Project"**
2. Name: **"michaelschiemer"**
3. Klicke auf **"Create"**
## Schritt 4: Inventory erstellen (Hosts definieren)
### 4.1 Inventory anlegen
1. Gehe zu **Inventories** ? **New Inventory**
2. Name: **"Production Hosts"**
3. Klicke auf **"Create"**
### 4.2 Host hinzuf?gen
1. Klicke auf dein Inventory ? **"Add Host"**
2. F?lle aus:
```
Name: production
Address: 94.16.110.151
SSH Username: deploy
SSH Port: 22
```
3. Klicke auf **"Save"**
### 4.3 SSH-Key hinzuf?gen
1. Gehe zu **Keys** (in der Seitenleiste)
2. Klicke auf **"New Key"**
3. Name: **"Deployment Key"**
4. F?ge deinen SSH Private Key ein (aus `~/.ssh/production`):
```bash
cat ~/.ssh/production
```
5. Klicke auf **"Save"**
6. Gehe zur?ck zu deinem Inventory und w?hle den Key bei deinem Host aus
## Schritt 5: Template erstellen (Playbook verwenden)
### 5.1 Template f?r CI Tests
1. Gehe zu **Templates** ? **New Template**
2. **Name**: "Run CI Tests"
3. **Inventory**: W?hle "Production Hosts"
4. **Playbook Path**: `/tmp/semaphore/playbooks/ci-tests.yml`
5. **Variables** (klicke auf "Variables"):
```yaml
repo_url: https://git.michaelschiemer.de/michael/michaelschiemer.git
repo_branch: main
```
6. Klicke auf **"Save"**
### 5.2 Template f?r Docker Build
1. **Templates** ? **New Template**
2. **Name**: "Build Docker Image"
3. **Inventory**: "Production Hosts"
4. **Playbook Path**: `/tmp/semaphore/playbooks/docker-build.yml`
5. **Variables**:
```yaml
repo_url: https://git.michaelschiemer.de/michael/michaelschiemer.git
repo_branch: main
registry_url: registry.michaelschiemer.de
registry_user: admin
registry_password: <dein-registry-passwort>
image_name: framework
image_tag: latest
```
6. **Save**
### 5.3 Template f?r Staging Deployment
1. **Templates** ? **New Template**
2. **Name**: "Deploy to Staging"
3. **Inventory**: "Production Hosts"
4. **Playbook Path**: `/tmp/semaphore/playbooks/deploy-staging.yml`
5. **Variables**:
```yaml
registry_url: registry.michaelschiemer.de
registry_user: admin
registry_password: <dein-registry-passwort>
image_name: framework
image_tag: latest
```
6. **Save**
### 5.4 Template f?r Production Deployment
1. **Templates** ? **New Template**
2. **Name**: "Deploy to Production"
3. **Inventory**: "Production Hosts"
4. **Playbook Path**: `/tmp/semaphore/playbooks/deploy-production.yml`
5. **Variables**:
```yaml
registry_url: registry.michaelschiemer.de
registry_user: admin
registry_password: <dein-registry-passwort>
image_name: framework
image_tag: latest
```
6. **Save**
## Schritt 6: Task ausf?hren
1. Gehe zu **Templates**
2. Klicke auf ein Template (z.B. "Run CI Tests")
3. Klicke auf **"Run"**
4. Beobachte die Ausf?hrung in Echtzeit
## ? Fertig!
Semaphore ist jetzt mit deinem Repository verbunden! Du kannst:
- ? CI/CD-Tasks manuell starten
- ? Playbooks aus dem Repository verwenden
- ? Deployments auf deine Server ausf?hren
## ?? Verf?gbare Playbooks
Die folgenden Playbooks sind bereits im Repository und k?nnen in Semaphore verwendet werden:
1. **`/tmp/semaphore/playbooks/ci-tests.yml`** - PHP Tests & Quality Checks
2. **`/tmp/semaphore/playbooks/docker-build.yml`** - Docker Image Build & Push
3. **`/tmp/semaphore/playbooks/deploy-staging.yml`** - Staging Deployment
4. **`/tmp/semaphore/playbooks/deploy-production.yml`** - Production Deployment
## ?? Vorhandene Ansible-Playbooks
Die vorhandenen Ansible-Playbooks aus `deployment/ansible/playbooks/` sind auch verf?gbar unter:
- `/tmp/semaphore/repo-playbooks/`
Zum Beispiel:
- `/tmp/semaphore/repo-playbooks/deploy-update.yml`
- `/tmp/semaphore/repo-playbooks/rollback.yml`
## ?? Tipps
### Docker Socket f?r Build-Tasks
Falls Docker-Build-Tasks ausgef?hrt werden sollen, f?ge den Docker Socket hinzu:
In `docker-compose.yml` bei Semaphore-Service:
```yaml
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
```
Dann Semaphore neu starten:
```bash
docker compose restart semaphore
```
### Git-Integration
Die Playbooks k?nnen direkt auf das Git-Repository zugreifen (HTTPS). F?r private Repositories oder SSH-Zugriff:
1. Erstelle einen SSH-Key f?r Git in Semaphore (Keys ? New Key)
2. F?ge den Public Key zu deinem Git-Repository hinzu
3. Verwende SSH-URL in Playbook-Variablen:
```yaml
repo_url: git@git.michaelschiemer.de:michael/michaelschiemer.git
```
---
**Hilfe**: Weitere Details in `SETUP_REPOSITORY.md` und `README.md`

View File

@@ -0,0 +1,576 @@
# Semaphore CI Stack - Lokale Entwicklung
## Übersicht
Selbst-gehostete Semaphore CI/CD-Plattform für lokale Entwicklung, die es ermöglicht, CI/CD-Pipelines und Ansible-Playbooks lokal zu testen und auszuführen, ohne Abhängigkeit von externen CI-Services.
**Features**:
- **Selbst-gehostet**: Läuft vollständig lokal auf dem Entwicklungsrechner
- **Lokal isoliert**: NUR über localhost (127.0.0.1) erreichbar - KEIN externer Zugriff aus Sicherheitsgründen!
- **MySQL-Backend**: Persistente Datenbank für Projekte, Tasks und Templates
- **Web-UI**: Intuitive Benutzeroberfläche für Pipeline-Management (nur lokal)
- **Ansible-Integration**: Native Unterstützung für Ansible-Playbooks
- **Docker-basiert**: Einfaches Setup und Wartung
**Einsatzzweck**:
- Lokales Testen von CI/CD-Pipelines
- Entwicklung und Test von Ansible-Playbooks
- Experimentieren mit Deployment-Workflows
- Keine Abhängigkeit von externen CI-Services
## Services
- **mysql** - MySQL 8.0 Datenbank für Semaphore-Daten
- **semaphore** - Semaphore CI/CD Web-UI und API
## Voraussetzungen
- Docker und Docker Compose installiert
- Port 9300 auf localhost frei verfügbar (3000 wird von Gitea verwendet)
- Ausreichend Speicherplatz für Docker Volumes (~500MB initial)
## Verzeichnisstruktur
```
semaphore/
├── docker-compose.yml # Service-Definitionen
├── env.example # Environment-Variablen Template
├── .env # Environment-Konfiguration (aus env.example erstellen)
└── README.md # Diese Datei
```
## Quick Start
### 1. Environment-Datei erstellen
```bash
cd deployment/stacks/semaphore
cp env.example .env
```
### 2. Konfiguration anpassen (Optional)
Bearbeite `.env` und passe die Werte an:
```bash
nano .env
```
**Wichtig**: Generiere einen sicheren Encryption Key:
```bash
# Linux/WSL
head -c32 /dev/urandom | base64
# Windows PowerShell
-join ((48..57) + (65..90) + (97..122) | Get-Random -Count 32 | % {[char]$_}) | ConvertTo-Base64
```
Aktualisiere `SEMAPHORE_ACCESS_KEY_ENCRYPTION` in der `.env`-Datei.
### 3. Stack starten
```bash
docker compose up -d
```
### 4. Semaphore Web-UI öffnen
Öffne im Browser: **http://localhost:9300**
⚠️ **WICHTIG**: Semaphore ist NUR lokal zugänglich (127.0.0.1). Es gibt KEINEN externen Zugriff aus Sicherheitsgründen.
**Standard-Login**:
- **Username**: `admin` (oder Wert aus `SEMAPHORE_ADMIN`)
- **Password**: `admin` (oder Wert aus `SEMAPHORE_ADMIN_PASSWORD`)
### 5. Erste Schritte in Semaphore
1. **Projekt erstellen**: Klicke auf "New Project" und erstelle ein neues Projekt
2. **Inventory anlegen**: Erstelle ein Inventory mit lokalen Hosts oder Docker-Containern
3. **Template erstellen**: Erstelle ein Template mit einem Ansible-Playbook
4. **Task ausführen**: Starte einen Task und beobachte die Ausführung
## Konfiguration
### Environment-Variablen (.env)
#### MySQL-Datenbank
```env
MYSQL_ROOT_PASSWORD=semaphore_root
MYSQL_DATABASE=semaphore
MYSQL_USER=semaphore
MYSQL_PASSWORD=semaphore
```
#### Semaphore-Konfiguration
```env
# Port-Binding (Standard: 9300)
SEMAPHORE_PORT=9300
# Admin-Benutzer
SEMAPHORE_ADMIN=admin
SEMAPHORE_ADMIN_NAME=Administrator
SEMAPHORE_ADMIN_EMAIL=admin@localhost
SEMAPHORE_ADMIN_PASSWORD=admin
# Playbook-Pfad (im Container)
SEMAPHORE_PLAYBOOK_PATH=/tmp/semaphore
# Encryption Key (WICHTIG: Für Produktion ändern!)
SEMAPHORE_ACCESS_KEY_ENCRYPTION=change-me-in-production
```
#### Optionale Konfiguration
**LDAP-Integration** (Standard: deaktiviert):
```env
SEMAPHORE_LDAP_ENABLED=true
SEMAPHORE_LDAP_HOST=ldap.example.com
SEMAPHORE_LDAP_PORT=389
SEMAPHORE_LDAP_DN=cn=admin,dc=example,dc=com
SEMAPHORE_LDAP_PASSWORD=ldap_password
SEMAPHORE_LDAP_BASE_DN=dc=example,dc=com
SEMAPHORE_LDAP_USER_FILTER=(uid=%s)
```
**Webhook-Integration**:
```env
SEMAPHORE_WEBHOOK_URL=http://localhost:8080/webhook
```
## Verwendung
### Stack starten
```bash
# Services im Hintergrund starten
docker compose up -d
# Logs anzeigen
docker compose logs -f
# Nur Semaphore-Logs
docker compose logs -f semaphore
# Nur MySQL-Logs
docker compose logs -f mysql
```
### Stack stoppen
```bash
docker compose down
```
### Stack neu starten
```bash
docker compose restart
```
### Status prüfen
```bash
# Container-Status anzeigen
docker compose ps
# Health-Check-Status
docker compose ps --format "table {{.Name}}\t{{.Status}}"
# Semaphore-Health-Check manuell
docker compose exec semaphore wget --no-verbose --spider http://localhost:3000/api/health
```
### Datenbank-Backup
```bash
# MySQL-Dump erstellen
docker compose exec mysql mysqldump -u semaphore -psemaphore semaphore > semaphore-backup-$(date +%Y%m%d).sql
# Backup wiederherstellen
docker compose exec -T mysql mysql -u semaphore -psemaphore semaphore < semaphore-backup-YYYYMMDD.sql
```
### Daten löschen und neu starten
```bash
# ⚠️ WARNUNG: Löscht alle Daten!
docker compose down -v
docker compose up -d
```
## Erste Schritte mit Semaphore
### 1. Projekt erstellen
1. Öffne http://localhost:9300 im Browser
2. Melde dich mit Admin-Credentials an
3. Klicke auf "New Project"
4. Gib einen Projektnamen ein (z.B. "My Project")
5. Klicke auf "Create"
### 2. Inventory anlegen
Ein Inventory definiert die Hosts, auf denen Playbooks ausgeführt werden sollen.
**Option A: Lokaler Host**
1. Gehe zu Projekt → Inventories → New Inventory
2. Name: "Local Hosts"
3. Hinzufügen von Host:
- Name: `localhost`
- Address: `127.0.0.1`
- SSH Username: `your-username`
- SSH Key: Füge deinen privaten SSH-Key hinzu
**Option B: Docker-Container**
1. Erstelle ein Inventory mit Docker-Hosts
2. Für Docker-in-Docker Support benötigst du zusätzliche Konfiguration
### 3. Template erstellen
Templates definieren welche Playbooks ausgeführt werden sollen.
1. Gehe zu Projekt → Templates → New Template
2. Template-Name: "Hello World"
3. Inventory: Wähle dein Inventory
4. Playbook: Erstelle ein einfaches Playbook:
```yaml
---
- hosts: all
gather_facts: no
tasks:
- name: Print hello world
debug:
msg: "Hello from Semaphore CI!"
```
5. Speichere das Template
### 4. Task ausführen
1. Gehe zu Templates
2. Klicke auf dein Template
3. Klicke auf "Run"
4. Beobachte die Ausführung in Echtzeit
## Integration mit bestehenden Stacks
### Verwendung mit lokaler Docker-Registry
Semaphore kann Docker-Images aus der lokalen Registry verwenden:
**In Ansible-Playbooks**:
```yaml
- name: Pull image from local registry
docker_image:
name: registry.michaelschiemer.de/framework:latest
source: pull
register: image_result
```
**Voraussetzung**: Der Semaphore-Container muss Zugriff auf den Docker-Socket oder die Registry haben.
### Verwendung mit bestehenden Ansible-Playbooks
1. Mounte deine Playbooks als Volume:
```yaml
volumes:
- /path/to/your/playbooks:/tmp/semaphore/playbooks:ro
```
2. Oder kopiere Playbooks in den Container:
```bash
docker compose exec semaphore mkdir -p /tmp/semaphore/my-playbook
docker cp my-playbook.yml semaphore:/tmp/semaphore/my-playbook/playbook.yml
```
3. Verweise im Template auf den Playbook-Pfad
## Troubleshooting
### Port-Konflikt (Port 3000 vs 9300)
**Problem**: Port 3000 ist standardmäßig von Gitea belegt, daher verwendet Semaphore Port 9300.
**Lösung**: Wenn du einen anderen Port verwenden möchtest, setze `SEMAPHORE_PORT` in der `.env` Datei:
```env
SEMAPHORE_PORT=8080 # Oder ein anderer freier Port
```
**Wichtig**: Der interne Container-Port bleibt immer 3000 - nur der externe Host-Port ändert sich.
### Semaphore startet nicht
**Prüfe Logs**:
```bash
docker compose logs semaphore
```
**Häufige Ursachen**:
- MySQL ist noch nicht bereit (warte auf Health-Check)
- Port 9300 ist bereits belegt: `netstat -tuln | grep 9300` (oder auf Windows: `netstat -ano | findstr :9300`)
- Falsche Datenbank-Credentials
**Lösung**:
```bash
# Prüfe MySQL-Status
docker compose ps mysql
# Prüfe Semaphore-Logs für DB-Verbindungsfehler
docker compose logs semaphore | grep -i database
# Restart wenn nötig
docker compose restart semaphore
```
### MySQL startet nicht
**Prüfe MySQL-Logs**:
```bash
docker compose logs mysql
```
**Häufige Ursachen**:
- Volume-Permissions-Probleme
- Port-Konflikte (unwahrscheinlich, da kein Port-Mapping)
**Lösung**:
```bash
# Prüfe Volume
docker volume inspect semaphore-mysql-data
# Cleanup und Neu-Start (⚠️ Datenverlust!)
docker compose down -v
docker compose up -d
```
### Login funktioniert nicht
**Standard-Credentials**:
- Username: `admin` (oder `SEMAPHORE_ADMIN` Wert)
- Password: `admin` (oder `SEMAPHORE_ADMIN_PASSWORD` Wert)
**Admin-Passwort zurücksetzen**:
1. Stoppe Semaphore: `docker compose stop semaphore`
2. Setze `SEMAPHORE_ADMIN_PASSWORD` in `.env` auf neues Passwort
3. Starte Semaphore: `docker compose up -d`
### Playbooks werden nicht gefunden
**Prüfe Playbook-Pfad**:
```bash
docker compose exec semaphore ls -la /tmp/semaphore
```
**Lösung**:
- Stelle sicher, dass `SEMAPHORE_PLAYBOOK_PATH` korrekt gesetzt ist
- Prüfe, ob Playbooks im richtigen Pfad liegen
- Stelle sicher, dass Datei-Berechtigungen korrekt sind
### Health-Check schlägt fehl
**Prüfe Health-Check**:
```bash
docker compose exec semaphore wget --no-verbose --spider http://localhost:3000/api/health
```
**Lösung**:
- Warte auf vollständigen Start (kann 1-2 Minuten dauern)
- Prüfe Logs: `docker compose logs semaphore`
- Restart wenn nötig: `docker compose restart semaphore`
## Sicherheit
### 🔒 Lokaler Zugriff (Nur localhost)
Semaphore ist absichtlich NUR lokal zugänglich aus Sicherheitsgründen:
- ✅ **Nur localhost-Zugriff**: Port 9300 gebunden an 127.0.0.1 (nicht 0.0.0.0)
- ✅ **Keine externen Netzwerke**: Kein externer Zugriff möglich
- ✅ **Keine Traefik-Integration**: Keine öffentliche Route konfiguriert
- ✅ **Isoliertes Netzwerk**: Nur internes Docker-Netzwerk
- ⚠️ **Standard-Passwörter**: Nur für lokale Entwicklung - ändern bei Bedarf
### ⚠️ KEINE Produktions-Nutzung über Internet!
**Semaphore sollte NICHT öffentlich zugänglich gemacht werden!**
Gründe:
1. **Sicherheitsrisiko**: Semaphore hat Zugriff auf SSH-Keys und Deployment-Credentials
2. **Privilegierter Zugriff**: Kann auf Produktions-Server zugreifen
3. **Keine Multi-Factor-Authentication**: Standardmäßig keine 2FA
4. **Exploits**: Selbst-gehostete Software kann Sicherheitslücken haben
### Für lokale Entwicklung (Empfohlen)
Wenn du Semaphore lokal nutzen willst:
1. **Starke Passwörter**: Ändere alle Passwörter in `.env`:
```env
SEMAPHORE_ADMIN_PASSWORD=<starkes-passwort>
MYSQL_PASSWORD=<starkes-passwort>
MYSQL_ROOT_PASSWORD=<starkes-passwort>
```
2. **Encryption Key**: Generiere einen sicheren Key:
```bash
head -c32 /dev/urandom | base64
```
Setze in `.env`: `SEMAPHORE_ACCESS_KEY_ENCRYPTION=<generierter-key>`
3. **SSH-Keys**: Stelle sicher, dass SSH-Keys sicher gespeichert sind
4. **Backup-Strategie**: Regelmäßige MySQL-Backups einrichten
5. **Resource Limits**: Füge Memory/CPU-Limits hinzu (optional)
## Wartung
### Regelmäßige Aufgaben
**Wöchentlich**:
- Logs auf Fehler prüfen: `docker compose logs --tail=100`
- Disk-Space prüfen: `docker system df`
- Backup erstellen (wenn wichtige Daten vorhanden)
**Monatlich**:
- Images aktualisieren: `docker compose pull && docker compose up -d`
- Alte Tasks in Semaphore aufräumen (über Web-UI)
### Updates
```bash
# Aktuelle Images herunterladen
docker compose pull
# Mit neuen Images neu starten
docker compose up -d
# Logs prüfen
docker compose logs -f
```
### Daten bereinigen
```bash
# Alte Docker-Images löschen
docker image prune -a
# Alte Volumes prüfen
docker volume ls
# ⚠️ Vorsicht: Löscht alle Semaphore-Daten!
docker compose down -v
```
## Backup und Wiederherstellung
### Backup erstellen
```bash
# MySQL-Dump
docker compose exec mysql mysqldump \
-u semaphore -psemaphore semaphore \
> semaphore-backup-$(date +%Y%m%d-%H%M%S).sql
# Volume-Backup (komplett)
docker run --rm \
-v semaphore-mysql-data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/semaphore-mysql-backup-$(date +%Y%m%d).tar.gz /data
```
### Wiederherstellung
```bash
# MySQL-Dump wiederherstellen
docker compose exec -T mysql mysql \
-u semaphore -psemaphore semaphore \
< semaphore-backup-YYYYMMDD.sql
# Volume wiederherstellen (⚠️ stoppt Container)
docker compose down
docker run --rm \
-v semaphore-mysql-data:/data \
-v $(pwd):/backup \
alpine sh -c "cd /data && tar xzf /backup/semaphore-mysql-backup-YYYYMMDD.tar.gz"
docker compose up -d
```
## Performance-Optimierung
### MySQL-Optimierung
Für bessere Performance kannst du MySQL-Konfiguration anpassen:
1. Erstelle `mysql/conf.d/my.cnf`:
```ini
[mysqld]
innodb_buffer_pool_size = 256M
max_connections = 100
```
2. Mounte in `docker-compose.yml`:
```yaml
volumes:
- ./mysql/conf.d:/etc/mysql/conf.d:ro
```
### Resource Limits
Füge Limits in `docker-compose.yml` hinzu:
```yaml
deploy:
resources:
limits:
memory: 1G
cpus: '0.5'
```
## Unterstützung
### Dokumentation
- **Semaphore CI Docs**: https://docs.semaphoreui.com/
- **Semaphore GitHub**: https://github.com/semaphoreui/semaphore
### Logs
```bash
# Alle Logs
docker compose logs -f
# Semaphore-Logs
docker compose logs -f semaphore
# MySQL-Logs
docker compose logs -f mysql
# Letzte 100 Zeilen
docker compose logs --tail=100
```
### Health-Checks
```bash
# Container-Status
docker compose ps
# Semaphore-Health
curl http://localhost:9300/api/health
# MySQL-Health
docker compose exec mysql mysqladmin ping -h localhost -u root -psemaphore_root
```
---
**Setup-Status**: ✅ Bereit für lokale Entwicklung
**Nächste Schritte**:
1. `cp env.example .env` ausführen
2. `docker compose up -d` starten
3. http://localhost:9300 öffnen
4. Mit Admin-Credentials anmelden
5. Erstes Projekt und Template erstellen

View File

@@ -0,0 +1,355 @@
# Git-Repository in Semaphore Self-Hosted integrieren
Diese Anleitung beschreibt, wie du dein Git-Repository in Semaphore Self-Hosted (Ansible-UI) integrierst.
## ?? ?bersicht
Semaphore Self-Hosted ist eine Web-UI f?r Ansible, die es erm?glicht:
- Ansible-Playbooks ?ber eine grafische Oberfl?che auszuf?hren
- CI/CD-Workflows mit Ansible zu automatisieren
- Git-Repositories als Playbook-Quellen zu verwenden
## ?? Schritt 1: Semaphore starten
### 1.1 Environment-Datei erstellen
```bash
cd deployment/stacks/semaphore
cp env.example .env
```
### 1.2 Encryption Key generieren (WICHTIG!)
```bash
# Linux/WSL
head -c32 /dev/urandom | base64
# Windows PowerShell
-join ((48..57) + (65..90) + (97..122) | Get-Random -Count 32 | % {[char]$_}) | ConvertTo-Base64
```
Kopiere den generierten Key und setze ihn in `.env`:
```env
SEMAPHORE_ACCESS_KEY_ENCRYPTION=<dein-generierter-key>
```
### 1.3 Stack starten
```bash
docker compose up -d
```
### 1.4 Web-UI ?ffnen
?ffne im Browser: **http://localhost:9300**
?? **WICHTIG**: Semaphore ist NUR lokal zug?nglich (127.0.0.1). Es gibt KEINEN externen Zugriff aus Sicherheitsgr?nden!
**Standard-Login**:
- Username: `admin`
- Password: `admin`
## ?? Schritt 2: Projekt in Semaphore erstellen
### 2.1 Neues Projekt anlegen
1. Melde dich in Semaphore an
2. Klicke auf **"New Project"** oder **"Create Project"**
3. Gib einen Projektnamen ein: **"michaelschiemer"**
4. Klicke auf **"Create"**
### 2.2 Git-Repository als Playbook-Quelle hinzuf?gen
Semaphore Self-Hosted kann Playbooks direkt aus Git-Repositories laden. Du hast zwei Optionen:
**Option A: Playbooks aus Repository-Clone verwenden** (Empfohlen)
1. Clone dein Repository lokal:
```bash
git clone https://git.michaelschiemer.de/michael/michaelschiemer.git
cd michaelschiemer
```
2. Erstelle ein Playbook-Verzeichnis f?r Semaphore:
```bash
mkdir -p deployment/stacks/semaphore/playbooks
cp deployment/ansible/playbooks/*.yml deployment/stacks/semaphore/playbooks/
```
**Option B: Playbooks direkt in Semaphore einf?gen**
Semaphore kann auch Playbooks direkt im Web-UI erstellen/bearbeiten. F?r Git-Integration ist Option A besser.
## ??? Schritt 3: Inventory erstellen
Ein Inventory definiert die Hosts, auf denen Playbooks ausgef?hrt werden.
### 3.1 Inventory anlegen
1. Gehe zu deinem Projekt ? **Inventories** ? **New Inventory**
2. Name: **"Production Hosts"** oder **"Local Hosts"**
3. Klicke auf **"Create"**
### 3.2 Host hinzuf?gen
1. Klicke auf dein Inventory ? **Add Host**
2. F?lle folgende Felder aus:
**F?r Production-Deployment:**
```
Name: production
Address: 94.16.110.151
SSH Username: deploy
SSH Port: 22
```
**F?r Staging-Deployment:**
```
Name: staging
Address: 94.16.110.151 # oder dein Staging-Host
SSH Username: deploy
SSH Port: 22
```
### 3.3 SSH-Key hinzuf?gen
1. Gehe zu **Keys** (Seitenleiste)
2. Klicke auf **"New Key"**
3. Gib einen Namen ein: **"Deployment Key"**
4. F?ge deinen SSH Private Key ein (aus `~/.ssh/production` oder ?hnlich)
5. W?hle den Key im Host-Inventory aus
## ?? Schritt 4: Template erstellen
Templates verbinden Playbooks mit Inventories und definieren Parameter.
### 4.1 Template f?r Tests erstellen
1. Gehe zu **Templates** ? **New Template**
2. Template-Name: **"Run PHP Tests"**
3. Inventory: W?hle dein Inventory
4. Playbook: Erstelle ein Playbook oder verwende ein vorhandenes:
```yaml
---
- name: Run PHP Tests
hosts: localhost
gather_facts: no
tasks:
- name: Checkout repository
git:
repo: https://git.michaelschiemer.de/michael/michaelschiemer.git
dest: /tmp/ci-build
version: main
- name: Install dependencies
command: composer install --no-interaction --prefer-dist
args:
chdir: /tmp/ci-build
- name: Run tests
command: ./vendor/bin/pest
args:
chdir: /tmp/ci-build
register: test_result
- name: Show test results
debug:
var: test_result.stdout_lines
```
5. Speichere das Template
### 4.2 Template f?r Build erstellen
Erstelle ein Template f?r Docker Image Build:
```yaml
---
- name: Build and Push Docker Image
hosts: localhost
gather_facts: no
tasks:
- name: Checkout repository
git:
repo: https://git.michaelschiemer.de/michael/michaelschiemer.git
dest: /tmp/ci-build
version: main
- name: Login to Docker registry
docker_login:
username: "{{ registry_user }}"
password: "{{ registry_password }}"
registry_url: "{{ registry_url }}"
vars:
registry_user: "admin"
registry_url: "registry.michaelschiemer.de"
- name: Build Docker image
docker_image:
name: "{{ registry_url }}/framework:{{ image_tag }}"
tag: "{{ image_tag }}"
source: build
build:
path: /tmp/ci-build
dockerfile: Dockerfile.production
push: yes
vars:
registry_url: "registry.michaelschiemer.de"
image_tag: "latest"
```
### 4.3 Template f?r Deployment erstellen
Erstelle ein Template f?r Production-Deployment (verwendet die vorhandenen Ansible-Playbooks):
```yaml
---
- name: Deploy to Production
hosts: production
gather_facts: yes
become: yes
tasks:
- name: Checkout deployment scripts
git:
repo: https://git.michaelschiemer.de/michael/michaelschiemer.git
dest: /tmp/deployment
version: main
- name: Include deployment playbook
include_role:
name: deployment/ansible/playbooks/deploy-update.yml
```
Oder verwende die vorhandenen Ansible-Playbooks direkt:
**Vorteil**: Die vorhandenen Playbooks in `deployment/ansible/playbooks/` k?nnen direkt verwendet werden!
1. Erstelle ein Template: **"Deploy to Production"**
2. W?hle Inventory: **"Production Hosts"**
3. Playbook-Pfad: **`deployment/ansible/playbooks/deploy-update.yml`**
4. Speichere
## ?? Schritt 5: CI/CD-Workflow einrichten
### 5.1 Git-Webhook konfigurieren (Optional)
Semaphore kann Webhooks von Git-Repositories empfangen, um automatisch Tasks zu starten.
1. Gehe zu deinem Git-Repository (Gitea)
2. Settings ? Webhooks ? Add Webhook
3. Webhook-URL: `http://localhost:9300/api/hook/git` (nur lokal!)
4. Content-Type: `application/json`
5. Secret: Optional, aber empfohlen
### 5.2 Task manuell starten
1. Gehe zu **Templates**
2. Klicke auf dein Template (z.B. "Run PHP Tests")
3. Klicke auf **"Run"**
4. Beobachte die Ausf?hrung in Echtzeit
### 5.3 Task automatisch starten
Semaphore kann Tasks basierend auf Git-Events starten:
1. Gehe zu Template ? **Settings**
2. Aktiviere **"Auto Run on Push"**
3. W?hle Branch: `main` oder `staging`
4. Speichere
## ?? Verwendung vorhandener Ansible-Playbooks
### Vorhandene Playbooks verwenden
Die vorhandenen Ansible-Playbooks in `deployment/ansible/playbooks/` k?nnen direkt in Semaphore verwendet werden:
1. **Mounte Playbooks als Volume** (in `docker-compose.yml`):
```yaml
volumes:
- ./../../ansible/playbooks:/tmp/semaphore/playbooks:ro
```
2. **Erstelle Templates**, die auf diese Playbooks verweisen:
- Playbook-Pfad: `/tmp/semaphore/playbooks/deploy-update.yml`
- Inventory: W?hle dein Production-Inventory
### Beispiel-Templates
#### Template: Deploy Update
- Name: "Deploy Update"
- Inventory: "Production Hosts"
- Playbook: `/tmp/semaphore/playbooks/deploy-update.yml`
- Variables:
```yaml
registry_url: registry.michaelschiemer.de
image_name: framework
image_tag: latest
```
#### Template: Rollback
- Name: "Rollback"
- Inventory: "Production Hosts"
- Playbook: `/tmp/semaphore/playbooks/rollback.yml`
- Variables:
```yaml
registry_url: registry.michaelschiemer.de
image_name: framework
```
## ?? Erweiterte Konfiguration
### Docker Socket f?r Build-Tasks
F?r Docker-Build-Tasks muss der Semaphore-Container Zugriff auf den Docker-Socket haben:
**In `docker-compose.yml` hinzuf?gen:**
```yaml
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
```
### Environment-Variablen f?r Templates
Du kannst Environment-Variablen in Templates verwenden:
1. Gehe zu Template ? **Variables**
2. F?ge Variablen hinzu:
- `registry_url`: `registry.michaelschiemer.de`
- `registry_user`: `admin`
- `image_name`: `framework`
### Git-Integration mit SSH-Keys
F?r private Repositories:
1. Gehe zu **Keys**
2. Erstelle einen SSH-Key f?r Git-Access
3. F?ge den Public Key zu deinem Git-Repository hinzu (Deploy Keys)
4. Verwende SSH-URL in Git-Tasks:
```yaml
git:
repo: git@git.michaelschiemer.de:michael/michaelschiemer.git
```
## ?? N?tzliche Links
- [Semaphore Self-Hosted Dokumentation](https://docs.ansible-semaphore.com/)
- [Ansible Semaphore GitHub](https://github.com/ansible-semaphore/semaphore)
- [Ansible Playbook Dokumentation](https://docs.ansible.com/ansible/latest/playbook_guide/index.html)
## ? Checkliste
- [ ] Semaphore gestartet (http://localhost:9300)
- [ ] Projekt erstellt
- [ ] Inventory mit Hosts erstellt
- [ ] SSH-Keys f?r Host-Zugriff konfiguriert
- [ ] Template f?r Tests erstellt
- [ ] Template f?r Build erstellt
- [ ] Template f?r Deployment erstellt
- [ ] Erstes Template erfolgreich ausgef?hrt
- [ ] Git-Webhook konfiguriert (optional)
---
**N?chste Schritte**: Starte Semaphore und erstelle dein erstes Template!

View File

@@ -0,0 +1,93 @@
services:
# MySQL Database for Semaphore
mysql:
image: mysql:8.0
container_name: semaphore-mysql
restart: unless-stopped
networks:
- semaphore-internal
environment:
- TZ=Europe/Berlin
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-semaphore_root}
- MYSQL_DATABASE=${MYSQL_DATABASE:-semaphore}
- MYSQL_USER=${MYSQL_USER:-semaphore}
- MYSQL_PASSWORD=${MYSQL_PASSWORD:-semaphore}
volumes:
- semaphore-mysql-data:/var/lib/mysql
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD:-semaphore_root}"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
command: >
--default-authentication-plugin=mysql_native_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
# Semaphore CI/CD Platform
semaphore:
image: semaphoreui/semaphore:latest
container_name: semaphore
restart: unless-stopped
depends_on:
mysql:
condition: service_healthy
networks:
- semaphore-internal
ports:
# ONLY bind to localhost (127.0.0.1) - NOT accessible externally!
# Default port 9300 to avoid conflict with Gitea (port 3000)
# SECURITY: This ensures Semaphore is only accessible locally
- "127.0.0.1:${SEMAPHORE_PORT:-9300}:3000"
# NO Traefik labels - Semaphore should only be accessible locally!
# External access is disabled for security reasons.
environment:
- TZ=Europe/Berlin
# Database Configuration
- SEMAPHORE_DB_DIALECT=mysql
- SEMAPHORE_DB_HOST=mysql
- SEMAPHORE_DB_PORT=3306
- SEMAPHORE_DB=${MYSQL_DATABASE:-semaphore}
- SEMAPHORE_DB_USER=${MYSQL_USER:-semaphore}
- SEMAPHORE_DB_PASS=${MYSQL_PASSWORD:-semaphore}
# Admin Configuration
- SEMAPHORE_ADMIN=${SEMAPHORE_ADMIN:-admin}
- SEMAPHORE_ADMIN_NAME=${SEMAPHORE_ADMIN_NAME:-Administrator}
- SEMAPHORE_ADMIN_EMAIL=${SEMAPHORE_ADMIN_EMAIL:-admin@localhost}
- SEMAPHORE_ADMIN_PASSWORD=${SEMAPHORE_ADMIN_PASSWORD:-admin}
# Playbook Path
- SEMAPHORE_PLAYBOOK_PATH=${SEMAPHORE_PLAYBOOK_PATH:-/tmp/semaphore}
# Encryption Key (generate with: head -c32 /dev/urandom | base64)
- SEMAPHORE_ACCESS_KEY_ENCRYPTION=${SEMAPHORE_ACCESS_KEY_ENCRYPTION:-change-me-in-production}
# Optional: LDAP Configuration (disabled by default)
- SEMAPHORE_LDAP_ENABLED=${SEMAPHORE_LDAP_ENABLED:-false}
# Optional: Webhook Configuration
- SEMAPHORE_WEBHOOK_URL=${SEMAPHORE_WEBHOOK_URL:-}
volumes:
- semaphore-data:/etc/semaphore
# Mount playbooks from repository so Semaphore can access them
- ../../../deployment/stacks/semaphore/playbooks:/tmp/semaphore/playbooks:ro
- ../../../deployment/ansible/playbooks:/tmp/semaphore/repo-playbooks:ro
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
volumes:
semaphore-mysql-data:
name: semaphore-mysql-data
semaphore-data:
name: semaphore-data
networks:
semaphore-internal:
name: semaphore-internal
driver: bridge

View File

@@ -0,0 +1,51 @@
# Semaphore CI Stack - Environment Configuration
# Copy this file to .env and adjust values as needed
# Note: Rename this file to .env.example if you prefer the standard naming
# ============================================
# MySQL Database Configuration
# ============================================
MYSQL_ROOT_PASSWORD=semaphore_root
MYSQL_DATABASE=semaphore
MYSQL_USER=semaphore
MYSQL_PASSWORD=semaphore
# ============================================
# Semaphore Configuration
# ============================================
# Port binding (default: 9300)
# Only accessible via localhost (127.0.0.1)
# Note: Changed from 3000 to avoid conflict with Gitea
SEMAPHORE_PORT=9300
# Admin User Configuration
SEMAPHORE_ADMIN=admin
SEMAPHORE_ADMIN_NAME=Administrator
SEMAPHORE_ADMIN_EMAIL=admin@localhost
SEMAPHORE_ADMIN_PASSWORD=admin
# Playbook Storage Path (inside container)
SEMAPHORE_PLAYBOOK_PATH=/tmp/semaphore
# Access Key Encryption
# Generate with: head -c32 /dev/urandom | base64
# IMPORTANT: Change this in production!
SEMAPHORE_ACCESS_KEY_ENCRYPTION=change-me-in-production
# ============================================
# Optional: LDAP Configuration
# ============================================
# SEMAPHORE_LDAP_ENABLED=false
# SEMAPHORE_LDAP_HOST=ldap.example.com
# SEMAPHORE_LDAP_PORT=389
# SEMAPHORE_LDAP_DN=cn=admin,dc=example,dc=com
# SEMAPHORE_LDAP_PASSWORD=ldap_password
# SEMAPHORE_LDAP_BASE_DN=dc=example,dc=com
# SEMAPHORE_LDAP_USER_FILTER=(uid=%s)
# ============================================
# Optional: Webhook Configuration
# ============================================
# SEMAPHORE_WEBHOOK_URL=http://localhost:8080/webhook

View File

@@ -0,0 +1,103 @@
---
# CI Tests Playbook f?r Semaphore
# F?hrt PHP Tests und Quality Checks aus
- name: Run CI Tests and Quality Checks
hosts: localhost
gather_facts: no
vars:
repo_url: "{{ repo_url | default('https://git.michaelschiemer.de/michael/michaelschiemer.git') }}"
repo_branch: "{{ repo_branch | default('main') }}"
build_dir: "/tmp/ci-build"
tasks:
- name: Clean up build directory
file:
path: "{{ build_dir }}"
state: absent
- name: Checkout repository
git:
repo: "{{ repo_url }}"
dest: "{{ build_dir }}"
version: "{{ repo_branch }}"
force: yes
register: git_result
- name: Display checked out commit
debug:
msg: "Checked out commit: {{ git_result.after }}"
- name: Install Composer if not present
get_url:
url: https://getcomposer.org/installer
dest: /tmp/composer-installer.php
mode: '0755'
when: ansible_facts.os_family == "Debian"
- name: Install Composer (Debian)
shell: php /tmp/composer-installer.php && mv composer.phar /usr/local/bin/composer
when: ansible_facts.os_family == "Debian"
args:
creates: /usr/local/bin/composer
- name: Install PHP dependencies
command: composer install --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-req=php
args:
chdir: "{{ build_dir }}"
register: composer_result
- name: Display composer output
debug:
var: composer_result.stdout_lines
when: composer_result.stdout_lines is defined
- name: Run PHP tests
command: ./vendor/bin/pest
args:
chdir: "{{ build_dir }}"
register: test_result
ignore_errors: yes
- name: Display test results
debug:
msg: "{{ test_result.stdout_lines }}"
when: test_result.stdout_lines is defined
- name: Run PHPStan
command: composer phpstan
args:
chdir: "{{ build_dir }}"
register: phpstan_result
ignore_errors: yes
when: test_result.rc == 0
- name: Display PHPStan results
debug:
msg: "{{ phpstan_result.stdout_lines }}"
when: phpstan_result.stdout_lines is defined and phpstan_result.rc == 0
- name: Run code style check
command: composer cs
args:
chdir: "{{ build_dir }}"
register: cs_result
ignore_errors: yes
when: test_result.rc == 0
- name: Display code style results
debug:
msg: "{{ cs_result.stdout_lines }}"
when: cs_result.stdout_lines is defined
- name: Fail if tests failed
fail:
msg: "Tests failed! Check output above."
when: test_result.rc != 0
- name: Summary
debug:
msg:
- "? CI Tests completed successfully!"
- "Commit: {{ git_result.after }}"
- "Branch: {{ repo_branch }}"

View File

@@ -0,0 +1,129 @@
---
# Production Deployment Playbook f?r Semaphore
# Deployed die Anwendung auf Production-Server
- name: Deploy to Production
hosts: production
gather_facts: yes
become: yes
vars:
registry_url: "{{ registry_url | default('registry.michaelschiemer.de') }}"
image_name: "{{ image_name | default('framework') }}"
image_tag: "{{ image_tag | default('latest') }}"
registry_user: "{{ registry_user | default('admin') }}"
registry_password: "{{ registry_password | required }}"
deployment_path: "{{ deployment_path | default('~/deployment/stacks/application') }}"
repo_url: "{{ repo_url | default('https://git.michaelschiemer.de/michael/michaelschiemer.git') }}"
tasks:
- name: Display deployment info
debug:
msg:
- "?? Starting production deployment..."
- "Host: {{ inventory_hostname }}"
- "Registry: {{ registry_url }}"
- "Image: {{ image_name }}:{{ image_tag }}"
- "Path: {{ deployment_path }}"
- name: Ensure deployment directory exists
file:
path: "{{ deployment_path }}"
state: directory
mode: '0755'
- name: Login to Docker registry
docker_login:
username: "{{ registry_user }}"
password: "{{ registry_password }}"
registry_url: "{{ registry_url }}"
- name: Pull Docker image
docker_image:
name: "{{ registry_url }}/{{ image_name }}:{{ image_tag }}"
source: pull
- name: Check if docker-compose files exist
stat:
path: "{{ deployment_path }}/docker-compose.base.yml"
register: base_compose_exists
- name: Check if docker-compose.production.yml exists
stat:
path: "{{ deployment_path }}/docker-compose.production.yml"
register: production_compose_exists
- name: Copy docker-compose files if missing
copy:
src: "{{ item.src }}"
dest: "{{ deployment_path }}/{{ item.dest }}"
mode: '0644'
loop:
- { src: "docker-compose.base.yml", dest: "docker-compose.base.yml" }
- { src: "docker-compose.production.yml", dest: "docker-compose.production.yml" }
when: not (item.dest == "docker-compose.base.yml" and base_compose_exists.stat.exists) or
not (item.dest == "docker-compose.production.yml" and production_compose_exists.stat.exists)
delegate_to: localhost
become: no
- name: Update docker-compose.production.yml with new image
replace:
path: "{{ deployment_path }}/docker-compose.production.yml"
regexp: 'image:.*{{ image_name }}:.*'
replace: 'image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}'
- name: Update docker-compose.production.yml with new image (alternative format)
replace:
path: "{{ deployment_path }}/docker-compose.production.yml"
regexp: 'image:.*{{ image_name }}@.*'
replace: 'image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}'
- name: Ensure Docker networks exist
docker_network:
name: traefik-public
state: present
ignore_errors: yes
- name: Deploy services
docker_compose_v2:
project_src: "{{ deployment_path }}"
files:
- "{{ deployment_path }}/docker-compose.base.yml"
- "{{ deployment_path }}/docker-compose.production.yml"
pull: missing
state: present
recreate: always
- name: Wait for services to start
pause:
seconds: 15
- name: Check container status
command: docker compose -f {{ deployment_path }}/docker-compose.base.yml -f {{ deployment_path }}/docker-compose.production.yml ps
register: container_status
- name: Display container status
debug:
var: container_status.stdout_lines
- name: Health check
uri:
url: https://michaelschiemer.de/health
method: GET
status_code: [200, 201, 204]
validate_certs: no
register: health_result
retries: 10
delay: 10
until: health_result.status == 200
ignore_errors: yes
- name: Display health check result
debug:
msg: "? Health check passed!" if health_result.status == 200 else "?? Health check failed"
- name: Summary
debug:
msg:
- "? Production deployment completed!"
- "Image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}"
- "URL: https://michaelschiemer.de"

View File

@@ -0,0 +1,109 @@
---
# Staging Deployment Playbook f?r Semaphore
# Deployed die Anwendung auf Staging-Server
- name: Deploy to Staging
hosts: staging
gather_facts: yes
become: yes
vars:
registry_url: "{{ registry_url | default('registry.michaelschiemer.de') }}"
image_name: "{{ image_name | default('framework') }}"
image_tag: "{{ image_tag | default('latest') }}"
registry_user: "{{ registry_user | default('admin') }}"
registry_password: "{{ registry_password | required }}"
deployment_path: "{{ deployment_path | default('~/deployment/stacks/staging') }}"
repo_url: "{{ repo_url | default('https://git.michaelschiemer.de/michael/michaelschiemer.git') }}"
tasks:
- name: Display deployment info
debug:
msg:
- "?? Starting staging deployment..."
- "Host: {{ inventory_hostname }}"
- "Registry: {{ registry_url }}"
- "Image: {{ image_name }}:{{ image_tag }}"
- "Path: {{ deployment_path }}"
- name: Ensure deployment directory exists
file:
path: "{{ deployment_path }}"
state: directory
mode: '0755'
- name: Login to Docker registry
docker_login:
username: "{{ registry_user }}"
password: "{{ registry_password }}"
registry_url: "{{ registry_url }}"
- name: Pull Docker image
docker_image:
name: "{{ registry_url }}/{{ image_name }}:{{ image_tag }}"
source: pull
- name: Check if docker-compose files exist
stat:
path: "{{ deployment_path }}/docker-compose.base.yml"
register: base_compose_exists
- name: Check if docker-compose.staging.yml exists
stat:
path: "{{ deployment_path }}/docker-compose.staging.yml"
register: staging_compose_exists
- name: Copy docker-compose files (always update)
copy:
src: "{{ item.src }}"
dest: "{{ deployment_path }}/{{ item.dest }}"
mode: '0644'
force: yes
loop:
- { src: "docker-compose.base.yml", dest: "docker-compose.base.yml" }
- { src: "docker-compose.staging.yml", dest: "docker-compose.staging.yml" }
delegate_to: localhost
become: no
- name: Update docker-compose.staging.yml with new image
replace:
path: "{{ deployment_path }}/docker-compose.staging.yml"
regexp: 'image:.*{{ image_name }}:.*'
replace: 'image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}'
- name: Ensure Docker networks exist
docker_network:
name: "{{ item }}"
state: present
loop:
- traefik-public
- staging-internal
ignore_errors: yes
- name: Deploy services
docker_compose_v2:
project_src: "{{ deployment_path }}"
files:
- "{{ deployment_path }}/docker-compose.base.yml"
- "{{ deployment_path }}/docker-compose.staging.yml"
pull: missing
state: present
recreate: always
- name: Wait for services to start
pause:
seconds: 15
- name: Check container status
command: docker compose -f {{ deployment_path }}/docker-compose.base.yml -f {{ deployment_path }}/docker-compose.staging.yml ps
register: container_status
- name: Display container status
debug:
var: container_status.stdout_lines
- name: Summary
debug:
msg:
- "? Staging deployment completed!"
- "Image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}"
- "URL: https://staging.michaelschiemer.de"

View File

@@ -0,0 +1,103 @@
---
# Docker Build Playbook f?r Semaphore
# Baut Docker Image und pusht es zur Registry
- name: Build and Push Docker Image
hosts: localhost
gather_facts: no
vars:
repo_url: "{{ repo_url | default('https://git.michaelschiemer.de/michael/michaelschiemer.git') }}"
repo_branch: "{{ repo_branch | default('main') }}"
build_dir: "/tmp/ci-build"
registry_url: "{{ registry_url | default('registry.michaelschiemer.de') }}"
image_name: "{{ image_name | default('framework') }}"
image_tag: "{{ image_tag | default('latest') }}"
registry_user: "{{ registry_user | default('admin') }}"
registry_password: "{{ registry_password | required }}"
tasks:
- name: Clean up build directory
file:
path: "{{ build_dir }}"
state: absent
- name: Checkout repository
git:
repo: "{{ repo_url }}"
dest: "{{ build_dir }}"
version: "{{ repo_branch }}"
force: yes
register: git_result
- name: Get short commit SHA
shell: echo "{{ git_result.after }}" | cut -c1-7
register: short_sha
- name: Generate image tags
set_fact:
tags:
- "{{ registry_url }}/{{ image_name }}:latest"
- "{{ registry_url }}/{{ image_name }}:{{ image_tag }}"
- "{{ registry_url }}/{{ image_name }}:git-{{ short_sha.stdout }}"
- name: Display image tags
debug:
msg:
- "??? Building Docker image..."
- "Registry: {{ registry_url }}"
- "Image: {{ image_name }}"
- "Tags: {{ tags | join(', ') }}"
- name: Ensure Docker is available
command: docker --version
register: docker_version
- name: Display Docker version
debug:
msg: "Docker version: {{ docker_version.stdout }}"
- name: Login to Docker registry
docker_login:
username: "{{ registry_user }}"
password: "{{ registry_password }}"
registry_url: "{{ registry_url }}"
register: login_result
- name: Verify registry login
debug:
msg: "? Successfully logged in to {{ registry_url }}"
when: login_result.failed == false
- name: Build Docker image
docker_image:
name: "{{ registry_url }}/{{ image_name }}"
tag: "{{ image_tag }}"
source: build
build:
path: "{{ build_dir }}"
dockerfile: Dockerfile.production
push: yes
state: present
register: build_result
- name: Tag image with additional tags
docker_image:
name: "{{ registry_url }}/{{ image_name }}:{{ image_tag }}"
repository: "{{ registry_url }}/{{ image_name }}"
tag: "{{ item }}"
source: local
push: yes
state: present
loop:
- "latest"
- "git-{{ short_sha.stdout }}"
when: build_result.changed
- name: Summary
debug:
msg:
- "? Docker image built and pushed successfully!"
- "Registry: {{ registry_url }}"
- "Image: {{ image_name }}"
- "Tags: {{ tags | join(', ') }}"
- "Commit: {{ git_result.after }}"