Files
iwz/deploy.py
T

119 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""
IWZ Ferienwohnungen - Deploy Script
Baut die Hugo-Seite und deployt sie per FTP zur Bunny.net Storage Zone.
"""
import ftplib
import os
import subprocess
import sys
import urllib.error
import urllib.parse
import urllib.request
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parent
PUBLIC_DIR = PROJECT_ROOT / "public"
FTP_PORT = 21
def load_env(path):
env = {}
if not path.exists():
return env
for line in path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
env[key.strip()] = value.strip()
return env
def hugo_build():
print("\n-> Hugo Build...")
result = subprocess.run("hugo --gc --minify", shell=True)
if result.returncode != 0:
print("\nFEHLER: Hugo Build fehlgeschlagen!")
sys.exit(1)
def ftp_upload_dir(ftp, local_dir, remote_dir=""):
for entry in sorted(local_dir.iterdir()):
remote_path = f"{remote_dir}/{entry.name}" if remote_dir else entry.name
if entry.is_dir():
try:
ftp.mkd(remote_path)
except ftplib.error_perm:
pass # existiert bereits
ftp_upload_dir(ftp, entry, remote_path)
else:
with open(entry, "rb") as f:
ftp.storbinary(f"STOR {remote_path}", f)
print(f" {remote_path}")
def deploy_to_bunny(host, zone, password):
print(f"\n-> Deploy zu Bunny.net Storage Zone '{zone}'...")
with ftplib.FTP() as ftp:
ftp.connect(host, FTP_PORT)
ftp.login(zone, password)
ftp.set_pasv(True)
ftp_upload_dir(ftp, PUBLIC_DIR)
def purge_cdn_cache(pull_zone_url, api_key):
"""Leert den Bunny CDN-Cache der Pull Zone per Wildcard-URL-Purge.
Ohne Purge liefert das CDN unveraenderte Dateinamen (z.B. /css/style.css)
weiter aus dem Cache -> online sieht man die alte Version.
"""
if not pull_zone_url or not api_key:
print("\n-> CDN-Purge uebersprungen "
"(BUNNY_PULL_ZONE_URL / BUNNY_API_KEY nicht gesetzt).")
print(" Cache ggf. manuell im Bunny-Dashboard leeren.")
return
wildcard = pull_zone_url.rstrip("/") + "/*"
query = urllib.parse.urlencode({"url": wildcard, "async": "false"})
url = f"https://api.bunny.net/purge?{query}"
print(f"\n-> CDN-Cache leeren: {wildcard}")
req = urllib.request.Request(url, method="POST")
req.add_header("AccessKey", api_key)
req.add_header("Accept", "application/json")
try:
with urllib.request.urlopen(req, timeout=60) as resp:
if resp.status == 200:
print(" Cache geleert.")
else:
print(f" WARNUNG: unerwarteter Status {resp.status}")
except urllib.error.HTTPError as e:
print(f" WARNUNG: Purge fehlgeschlagen (HTTP {e.code}). "
"Ist BUNNY_API_KEY der Account-API-Key?")
except urllib.error.URLError as e:
print(f" WARNUNG: Purge fehlgeschlagen ({e.reason}).")
if __name__ == "__main__":
print("=== IWZ Deploy ===")
env = {**load_env(PROJECT_ROOT / ".env"), **os.environ}
zone = env.get("BUNNY_STORAGE_ZONE")
host = env.get("BUNNY_STORAGE_HOST", "storage.bunnycdn.com")
password = env.get("BUNNY_STORAGE_PASSWORD")
pull_zone_url = env.get("BUNNY_PULL_ZONE_URL")
api_key = env.get("BUNNY_API_KEY")
if not zone or not password:
print("\nFEHLER: BUNNY_STORAGE_ZONE / BUNNY_STORAGE_PASSWORD fehlen.")
print("Siehe .env.example -> .env kopieren und befuellen.")
sys.exit(1)
hugo_build()
deploy_to_bunny(host, zone, password)
purge_cdn_cache(pull_zone_url, api_key)
print("\nFertig! Seite ist live.")