Add deployment workflow (docker-compose, deploy.sh, webhook receiver)
This commit is contained in:
@@ -1,11 +1,3 @@
|
|||||||
# AufmaßWeb – Konfiguration
|
DATABASE_URL=postgresql://aufmass:aufmass_secret@localhost:5432/aufmassweb
|
||||||
# Kopiere diese Datei nach .env und passe die Werte an
|
SECRET_KEY=change-me-in-production
|
||||||
|
REGISTRATION_ENABLED=false
|
||||||
SECRET_KEY=ersetzen-mit-sicherem-schluessel-mindestens-32-zeichen
|
|
||||||
|
|
||||||
# SQLite (Entwicklung lokal):
|
|
||||||
# DATABASE_URL=sqlite:///data/aufmass.db
|
|
||||||
|
|
||||||
# PostgreSQL (Produktion / Docker):
|
|
||||||
DATABASE_URL=postgresql://user:password@host:5432/aufmassweb
|
|
||||||
FLASK_ENV=production
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
REPO_DIR="/opt/aufmassweb"
|
||||||
|
COMPOSE_FILE="docker-compose.deploy.yml"
|
||||||
|
LOG_FILE="/var/log/aufmassweb-deploy.log"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
log "=== Deployment gestartet ==="
|
||||||
|
|
||||||
|
cd "$REPO_DIR"
|
||||||
|
|
||||||
|
log "Pull von Gitea..."
|
||||||
|
git pull origin main 2>&1 | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
log "Docker Compose build..."
|
||||||
|
docker compose -f "$COMPOSE_FILE" build 2>&1 | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
log "Docker Compose up (Rolling-Restart)..."
|
||||||
|
docker compose -f "$COMPOSE_FILE" up -d --remove-orphans 2>&1 | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
log "Alte Images bereinigen..."
|
||||||
|
docker image prune -f 2>&1 | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
log "=== Deployment erfolgreich abgeschlossen ==="
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- pgdata:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: aufmassweb
|
||||||
|
POSTGRES_USER: aufmass
|
||||||
|
POSTGRES_PASSWORD: ${DB_PASSWORD:-aufmass_secret}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "pg_isready", "-U", "aufmass", "-d", "aufmassweb"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
web:
|
||||||
|
build: ./_aufmass_web
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "5000:5000"
|
||||||
|
volumes:
|
||||||
|
- ./_aufmass_web/data:/app/data
|
||||||
|
- ./daten:/app/daten
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgresql://aufmass:${DB_PASSWORD:-aufmass_secret}@postgres:5432/aufmassweb
|
||||||
|
SECRET_KEY: ${SECRET_KEY:-change-me-in-production}
|
||||||
|
REGISTRATION_ENABLED: "false"
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pgdata:
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
WEBHOOK_SECRET = os.environ.get("WEBHOOK_SECRET", "change-me")
|
||||||
|
DEPLOY_SCRIPT = os.environ.get("DEPLOY_SCRIPT", "/opt/aufmassweb/deploy.sh")
|
||||||
|
|
||||||
|
def verify_signature(payload, signature):
|
||||||
|
if not signature:
|
||||||
|
return False
|
||||||
|
expected = "sha256=" + hmac.new(WEBHOOK_SECRET.encode(), payload, hashlib.sha256).hexdigest()
|
||||||
|
return hmac.compare_digest(expected, signature)
|
||||||
|
|
||||||
|
@app.route("/webhook", methods=["POST"])
|
||||||
|
def webhook():
|
||||||
|
signature = request.headers.get("X-Gitea-Signature", "")
|
||||||
|
payload = request.get_data()
|
||||||
|
|
||||||
|
if not verify_signature(payload, signature):
|
||||||
|
return jsonify({"error": "invalid signature"}), 403
|
||||||
|
|
||||||
|
data = request.get_json(silent=True) or {}
|
||||||
|
ref = data.get("ref", "")
|
||||||
|
if ref != "refs/heads/main":
|
||||||
|
return jsonify({"message": f"ignored push to {ref}"}), 200
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
["bash", DEPLOY_SCRIPT],
|
||||||
|
capture_output=True, text=True, timeout=300
|
||||||
|
)
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"status": "ok" if result.returncode == 0 else "error",
|
||||||
|
"stdout": result.stdout,
|
||||||
|
"stderr": result.stderr,
|
||||||
|
}), (200 if result.returncode == 0 else 500)
|
||||||
|
|
||||||
|
@app.route("/health", methods=["GET"])
|
||||||
|
def health():
|
||||||
|
return jsonify({"status": "ok"})
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(host="0.0.0.0", port=5001)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Webhook Receiver für AufmaßWeb Deployment
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
WorkingDirectory=/opt/aufmassweb
|
||||||
|
ExecStart=/usr/bin/python3 /opt/aufmassweb/webhook_deploy.py
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
Environment=WEBHOOK_SECRET=change-me
|
||||||
|
Environment=DEPLOY_SCRIPT=/opt/aufmassweb/deploy.sh
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Reference in New Issue
Block a user