# ============================================================== # BAN YARO — Makefile # Deploy-Strategie: rsync vom Mac zur DS (kein git auf DS nötig) # Git wird nur lokal auf dem Mac und für Forgejo-Backup genutzt. # ============================================================== DS_HOST := ds DS_IP := 10.47.11.10 # Hinweis: NPM braucht 10.47.11.99 als Forward-IP (Macvlan-Shim), nicht .10 DS_SSH_PORT := 4711 DS_PATH := /volume1/docker/banyaro DS_PATH_STAGING := /volume1/docker/banyaro-staging CONTAINER := banyaro CONTAINER_STAGING:= banyaro-staging SERVICE := banyaro GIT_REMOTE := origin DOCKER := sudo /usr/local/bin/docker VERSION ?= $(shell grep '"version"' backend/static/manifest.json | grep -o '[0-9]*\.[0-9]*\.[0-9]*' | head -1) # tar: was NICHT auf die DS übertragen wird TAR_EXCLUDE := --exclude='.git' \ --exclude='./data' \ --exclude='./backend/venv' \ --exclude='./backend/__pycache__' \ --exclude='./.env' \ --exclude='./*.db' \ --exclude='./tiles' \ --exclude='./.DS_Store' .PHONY: help deploy deploy-clean staging release sync push restart build stop status \ logs logs-f shell db dev clean-cache check-ssh reports bump test tiles tiles-deploy album # ---------------------------------------------------------- # SSH-Prüfung — Abhängigkeit aller DS-Befehle # ---------------------------------------------------------- check-ssh: @if ! nc -z -w3 $(DS_IP) $(DS_SSH_PORT) 2>/dev/null; then \ echo ""; \ echo " ✗ DS nicht erreichbar ($(DS_IP):$(DS_SSH_PORT))"; \ echo ""; \ echo " SSH-Port $(DS_SSH_PORT) ist geschlossen oder DS offline."; \ echo " → Port in Fritz!Box / DS-Firewall öffnen, dann erneut versuchen."; \ echo ""; \ exit 1; \ fi # ---------------------------------------------------------- # Hilfe # ---------------------------------------------------------- help: @echo "" @echo " Ban Yaro — verfügbare Befehle:" @echo "" @echo " make deploy Sync + Rebuild + Neustart (mit Layer-Cache, häufigster Befehl)" @echo " make deploy-clean Wie deploy, aber --no-cache (bei requirements-Änderungen)" @echo " make sync Nur Dateien zur DS übertragen (ohne Rebuild)" @echo " make push Nur Git push zu Forgejo (Backup/Versionierung)" @echo " make restart Container neustarten (kein Rebuild)" @echo " make build Docker neu bauen (ohne Neustart)" @echo " make stop Container stoppen" @echo " make status Container-Status anzeigen" @echo "" @echo " make logs Letzte 100 Zeilen" @echo " make logs-f Live-Log-Stream" @echo " make shell Bash im Container" @echo " make db SQLite-Shell" @echo "" @echo " make dev Lokaler Dev-Server auf Mac (Port 8001)" @echo " make clean-cache SW-Cache-Version erhöhen + restart" @echo " make reports Quartalsberichte generieren + committen" @echo "" # ---------------------------------------------------------- # DEPLOY — der Haupt-Befehl # 1. Git push (Backup) # 2. rsync zum DS # 3. Docker rebuild + restart # ---------------------------------------------------------- deploy: check-ssh @echo "→ Git push (Backup)..." @git push $(GIT_REMOTE) main @echo "→ Sync zu DS..." @COPYFILE_DISABLE=1 tar czf - $(TAR_EXCLUDE) . | ssh $(DS_HOST) "tar xzf - -C $(DS_PATH)/" @echo "→ Docker rebuild + restart (mit Layer-Cache)..." @ssh $(DS_HOST) " \ cd $(DS_PATH) && \ $(DOCKER) compose down && \ $(DOCKER) compose build && \ $(DOCKER) compose up -d" @echo "" @echo " ✓ Deploy fertig. Letzte Logs:" @ssh $(DS_HOST) "$(DOCKER) logs $(CONTAINER) --tail=15" # ---------------------------------------------------------- # DEPLOY-CLEAN — erzwingt Neuaufbau ohne Layer-Cache # Nötig wenn: requirements.txt geändert, System-Pakete aktualisiert, # oder nach einem kaputten Image-State # ---------------------------------------------------------- deploy-clean: check-ssh @echo "→ Git push (Backup)..." @git push $(GIT_REMOTE) main @echo "→ Sync zu DS..." @COPYFILE_DISABLE=1 tar czf - $(TAR_EXCLUDE) . | ssh $(DS_HOST) "tar xzf - -C $(DS_PATH)/" @echo "→ Docker rebuild (--no-cache) + restart..." @ssh $(DS_HOST) " \ cd $(DS_PATH) && \ $(DOCKER) compose down && \ $(DOCKER) compose build --no-cache && \ $(DOCKER) compose up -d" @echo "" @echo " ✓ Deploy fertig. Letzte Logs:" @ssh $(DS_HOST) "$(DOCKER) logs $(CONTAINER) --tail=15" # ---------------------------------------------------------- # STAGING — develop-Branch auf Staging deployen (Port 3012) # ---------------------------------------------------------- staging: check-ssh @echo "→ Git push develop..." @git push $(GIT_REMOTE) develop @echo "→ Sync zu DS (Staging)..." @COPYFILE_DISABLE=1 tar czf - $(TAR_EXCLUDE) . | ssh $(DS_HOST) "tar xzf - -C $(DS_PATH_STAGING)/" @echo "→ Staging rebuild + restart..." @ssh $(DS_HOST) " \ cd $(DS_PATH_STAGING) && \ $(DOCKER) compose -f docker-compose.staging.yml down && \ $(DOCKER) compose -f docker-compose.staging.yml build && \ $(DOCKER) compose -f docker-compose.staging.yml up -d" @echo "" @echo " ✓ Staging fertig — https://staging.banyaro.app" @ssh $(DS_HOST) "$(DOCKER) logs $(CONTAINER_STAGING) --tail=10" # ---------------------------------------------------------- # STAGING-DB — Produktions-DB in Staging kopieren (interaktiv, braucht sudo) # Aufruf: make staging-db # ---------------------------------------------------------- staging-db: check-ssh @echo "→ Produktions-DB nach Staging kopieren..." @ssh -t $(DS_HOST) " \ sudo cp $(DS_PATH)/data/banyaro.db $(DS_PATH_STAGING)/data/banyaro.db && \ sudo chmod 666 $(DS_PATH_STAGING)/data/banyaro.db && \ echo '✓ DB kopiert'" # ---------------------------------------------------------- # TILES — DACH-Vektortiles (planetiler → PMTiles), lokal bauen + ausliefern # Voraussetzung: Docker Desktop läuft, osmium installiert (brew install osmium-tool). # make tiles DACH neu generieren (download → merge → planetiler) # make tiles-deploy dach.pmtiles auf Staging ausliefern (atomar) # make tiles-deploy ENV=prod dach.pmtiles auf Produktion ausliefern (atomar) # Monatlich neu generieren hält die Karte aktuell. Datei liegt im data-Volume, # NICHT im Image — wird per Range-Route (/tiles) ausgeliefert. # ---------------------------------------------------------- TILES_DIR := tiles/build # DACH + alle angrenzenden Länder (15). Reihenfolge egal — osmium merge -H + time-filter # dedupliziert Grenz-Nodes. Output bleibt dach.pmtiles (Frontend referenziert den Namen). TILES_REGIONS := germany austria switzerland france italy czech-republic poland \ slovakia hungary slovenia netherlands belgium luxembourg denmark liechtenstein PLANETILER_IMAGE := ghcr.io/onthegomap/planetiler:latest TILES_TARGET := $(if $(filter prod,$(ENV)),$(DS_PATH),$(DS_PATH_STAGING)) tiles: @mkdir -p $(TILES_DIR) @echo "→ Geofabrik-Extrakte laden ($(TILES_REGIONS))..." @for r in $(TILES_REGIONS); do \ echo " $$r"; \ curl -fsSL -o $(TILES_DIR)/$$r.osm.pbf https://download.geofabrik.de/europe/$$r-latest.osm.pbf; done @echo "→ merge (History) + time-filter dedup → dach.osm.pbf..." @# Geofabrik-Extrakte können versetzte Stände haben (z.B. germany älter als at/ch) → @# Grenz-Nodes mit abweichender Version. Als History mergen + auf 'jetzt' snapshotten @# liefert genau eine Version pro ID (planetiler braucht eindeutige, sortierte IDs). @osmium merge -H $(foreach r,$(TILES_REGIONS),$(TILES_DIR)/$(r).osm.pbf) -o $(TILES_DIR)/dach-hist.osm.pbf --overwrite @osmium time-filter $(TILES_DIR)/dach-hist.osm.pbf -o $(TILES_DIR)/dach.osm.pbf --overwrite @# History + Einzel-PBFs jetzt freigeben (spart ~Quellsumme an Spitzen-Plattenplatz vor planetiler). @rm -f $(TILES_DIR)/dach-hist.osm.pbf $(foreach r,$(TILES_REGIONS),$(TILES_DIR)/$(r).osm.pbf) @echo "→ planetiler → dach.pmtiles (disk-backed mmap)..." @docker run --rm -v "$(CURDIR)/$(TILES_DIR):/data" $(PLANETILER_IMAGE) \ --osm-path=/data/dach.osm.pbf --download --output=/data/dach.pmtiles --force \ --storage=mmap --nodemap-storage=mmap @echo "" @echo " ✓ Tiles gebaut:"; ls -lh $(TILES_DIR)/dach.pmtiles tiles-deploy: check-ssh @if [ ! -f $(TILES_DIR)/dach.pmtiles ]; then echo "❌ $(TILES_DIR)/dach.pmtiles fehlt — erst 'make tiles'"; exit 1; fi @echo "→ Ausliefern nach $(TILES_TARGET)/data/tiles/ (atomarer Swap)..." @ssh $(DS_HOST) "mkdir -p $(TILES_TARGET)/data/tiles" @scp -O $(TILES_DIR)/dach.pmtiles $(DS_HOST):$(TILES_TARGET)/data/tiles/dach.pmtiles.tmp @ssh $(DS_HOST) "mv -f $(TILES_TARGET)/data/tiles/dach.pmtiles.tmp $(TILES_TARGET)/data/tiles/dach.pmtiles" @echo " ✓ dach.pmtiles ausgeliefert ($(if $(filter prod,$(ENV)),PRODUKTION,Staging))" @# Cache-Bust: TILES_VER in map-gl-style.js hochzählen (sonst liefert der Browser bis 24h alte Tiles). @NEWVER=$$(date +%Y%m%d%H%M); \ sed -i '' "s/var TILES_VER = '[0-9]*';/var TILES_VER = '$$NEWVER';/" backend/static/js/map-gl-style.js; \ echo " ↻ TILES_VER → $$NEWVER — JETZT Frontend ausliefern: make bump && make $(if $(filter prod,$(ENV)),deploy,staging)" # ---------------------------------------------------------- # RELEASE — develop → main → Production (VERSION= pflichtangabe) # Beispiel: make release VERSION=1.1.0 # ---------------------------------------------------------- release: check-ssh @if [ -z "$(VERSION)" ]; then \ echo "❌ Bitte VERSION setzen: make release VERSION=1.1.0"; exit 1; fi @echo "→ Merge develop → main (v$(VERSION))..." @git checkout main @git merge develop --no-ff -m "Release v$(VERSION)" @sed -i '' 's/"version": "[^"]*"/"version": "$(VERSION)"/' backend/static/manifest.json @sed -i '' "s/const APP_VERSION = '[^']*'/const APP_VERSION = '$(VERSION)'/" backend/static/js/app.js @git add backend/static/manifest.json backend/static/js/app.js @git commit --amend --no-edit @git tag "v$(VERSION)" @git push $(GIT_REMOTE) main --tags @echo "→ Production deployen..." @$(MAKE) deploy @git checkout develop @git merge main @git push $(GIT_REMOTE) develop @echo "" @echo " ✓ Release v$(VERSION) veröffentlicht 🚀" # ---------------------------------------------------------- # SYNC — nur Dateien zur DS übertragen, kein Docker-Rebuild # ACHTUNG: ALLE Dateien (CSS/JS/HTML/Python) sind ins Image gebacken! # sync+restart reicht für NICHTS — immer: make deploy # ---------------------------------------------------------- sync: check-ssh @echo "→ Sync zu DS..." @COPYFILE_DISABLE=1 tar czf - $(TAR_EXCLUDE) . | ssh $(DS_HOST) "tar xzf - -C $(DS_PATH)/" @echo " ✓ Sync fertig." # ---------------------------------------------------------- # PUSH — nur Git, ohne DS-Deploy # ---------------------------------------------------------- push: git push $(GIT_REMOTE) main # ---------------------------------------------------------- # RESTART — kein Rebuild, nur Container neu starten # Reicht nur für Umgebungsvariablen-Änderungen (.env) # ---------------------------------------------------------- restart: check-ssh @ssh $(DS_HOST) " \ cd $(DS_PATH) && \ $(DOCKER) compose restart $(SERVICE)" @echo " ✓ Neugestartet." # ---------------------------------------------------------- # BUILD — nur Docker-Image neu bauen (mit Layer-Cache) # ---------------------------------------------------------- build: check-ssh @ssh $(DS_HOST) " \ cd $(DS_PATH) && \ $(DOCKER) compose build" # ---------------------------------------------------------- # STOP # ---------------------------------------------------------- stop: check-ssh @ssh $(DS_HOST) "cd $(DS_PATH) && $(DOCKER) compose down" @echo " ✓ Gestoppt." # ---------------------------------------------------------- # STATUS # ---------------------------------------------------------- status: check-ssh @ssh $(DS_HOST) "$(DOCKER) ps \ --filter name=$(CONTAINER) \ --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'" # ---------------------------------------------------------- # LOGS # ---------------------------------------------------------- logs: check-ssh @ssh $(DS_HOST) "$(DOCKER) logs $(CONTAINER) --tail=100" logs-f: check-ssh @ssh $(DS_HOST) "$(DOCKER) logs $(CONTAINER) -f" # ---------------------------------------------------------- # SHELL im Container # ---------------------------------------------------------- shell: check-ssh @ssh -t $(DS_HOST) "$(DOCKER) exec -it $(CONTAINER) bash" # ---------------------------------------------------------- # SQLite # ---------------------------------------------------------- db: check-ssh @ssh -t $(DS_HOST) "$(DOCKER) exec -it $(CONTAINER) \ sqlite3 /data/banyaro.db" # ---------------------------------------------------------- # DEV — lokaler Server auf Mac (kein Docker, kein DS) # ---------------------------------------------------------- dev: @cd backend && \ test -d venv || python3 -m venv venv && \ . venv/bin/activate && \ pip install -q -r requirements.txt && \ KI_MODE=off ENV=development \ JWT_SECRET=dev-secret \ DB_PATH=./dev.db \ MEDIA_DIR=$${MEDIA_DIR:-/tmp/banyaro-media} \ BREEDER_DOCS_DIR=$${BREEDER_DOCS_DIR:-/tmp/banyaro-breeder} \ uvicorn main:app --reload --port 8001 --host $${HOST:-127.0.0.1} # ---------------------------------------------------------- # REPORTS — Quartalsberichte generieren und committen # Berichte laufen im Container (DB-Zugriff), werden lokal gespeichert # ---------------------------------------------------------- REPORT_DATE := $(shell date +%Y-%m-%d) REPORT_SECTIONS := sicherheit funktionsumfang dateien nutzer partner server reports: check-ssh @mkdir -p reports @echo "→ Berichte generieren ($(REPORT_DATE))..." @for section in $(REPORT_SECTIONS); do \ echo " → $$section..."; \ ssh $(DS_HOST) "$(DOCKER) exec $(CONTAINER) python3 scripts/generate_reports.py $$section" \ > reports/$(REPORT_DATE)-$$section.md; \ done @echo "→ Berichte committen..." @git add reports/ @git diff --cached --quiet || git commit -m "Reports $(REPORT_DATE) — Quartalsbericht" @echo "" @echo " ✓ Alle Berichte erstellt und committed:" @for section in $(REPORT_SECTIONS); do \ echo " reports/$(REPORT_DATE)-$$section.md"; \ done # ---------------------------------------------------------- # BUMP — zentrale Versions-Erhöhung (VERSION-Datei + sw.js + app.js + index.html) # Aufruf: # make bump → liest aus VERSION, erhöht +1, schreibt zurück, propagiert in alle Frontend-Stellen # make bump APP_VER=2000 → setzt VERSION explizit auf 2000 # Backend liest APP_VER beim Startup aus VERSION (siehe main.py:_read_app_ver()). # ---------------------------------------------------------- bump: @if [ ! -f VERSION ]; then echo "0" > VERSION; fi @CUR=$$(cat VERSION | tr -d '[:space:]'); \ if [ -n "$(APP_VER)" ]; then NEW="$(APP_VER)"; else NEW=$$(($$CUR + 1)); fi; \ printf "%s" "$$NEW" > VERSION; \ sed -i.bak -E "s/const VER[[:space:]]*=[[:space:]]*'[0-9]+'/const VER = '$$NEW'/" backend/static/sw.js && rm -f backend/static/sw.js.bak; \ sed -i.bak -E "s/const APP_VER[[:space:]]*=[[:space:]]*'[0-9]+'/const APP_VER = '$$NEW'/" backend/static/js/app.js && rm -f backend/static/js/app.js.bak; \ sed -i.bak -E "s/\?v=[0-9]+/?v=$$NEW/g" backend/static/index.html && rm -f backend/static/index.html.bak; \ sed -i.bak -E "s/\?v=[0-9]+/?v=$$NEW/g" backend/static/landing.html && rm -f backend/static/landing.html.bak; \ echo " ✓ APP_VER $$CUR → $$NEW (VERSION, sw.js, app.js, index.html, landing.html aktualisiert)" # ---------------------------------------------------------- # ALBUM — die zwei Download-ZIPs (DE+EN) neu bauen (Cover, ID3-Tags, Liner Notes) # Nur nötig, wenn sich Songs/Cover/Beschreibung ändern. Braucht ImageMagick + ffmpeg. # ---------------------------------------------------------- album: @bash tools/album-build/build.sh # ---------------------------------------------------------- # TEST — Smoke-Tests gegen isolierte Test-DB (kein Docker, kein DS) # ---------------------------------------------------------- test: @cd backend && test -d venv || python3 -m venv venv @backend/venv/bin/pip install -q -r backend/requirements.txt pytest pytest-asyncio @backend/venv/bin/python -m pytest -q # ---------------------------------------------------------- # CACHE leeren — SW-Version erhöhen, dann restart # Nach größeren CSS/JS-Änderungen wenn SW gecacht hat # ---------------------------------------------------------- clean-cache: check-ssh @NEW_VER="by-v$$(date +%s)"; \ ssh $(DS_HOST) " \ sed -i \"s/by-v[0-9]*/$$NEW_VER/g\" \ $(DS_PATH)/backend/static/sw.js" @$(MAKE) restart @echo " ✓ Cache-Version erhöht."