Files
2026-09-24 17:57:54 +02:00

73 lines
2.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Baut das Upload-Paket für cPanel: nur die Dateien, die der Server braucht.
Aufruf (im Projektordner, beliebiges Betriebssystem)::
python scripts/build_deploy_zip.py # -> dist/glory_defense.zip
python scripts/build_deploy_zip.py -o paket.zip
Warum ein eigenes Skript statt „ZIP-Datei erstellen“: Windows PowerShell 5.1
(`Compress-Archive`) schreibt Pfade mit Backslash ins Archiv. Manche Entpacker unter
Linux erzeugen daraus flache Dateinamen wie ``app\\api.py`` – dann findet Passenger
das Paket ``app`` nicht. Außerdem landen hier garantiert weder ``.env`` (Geheimnisse)
noch ``instance/`` (Datenbank) noch Caches im Paket.
"""
from __future__ import annotations
import argparse
import sys
import zipfile
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parent.parent
DEFAULT_OUTPUT = PROJECT_ROOT / "dist" / "glory_defense.zip"
#: Einzeldateien und Ordner, die auf den Server gehören.
INCLUDE_FILES = ("passenger_wsgi.py", "requirements.txt", ".env.example")
INCLUDE_DIRS = ("app",)
#: Wird nie mitgepackt, auch wenn es unterhalb von INCLUDE_DIRS liegt.
EXCLUDED_DIR_NAMES = frozenset({"__pycache__", ".pytest_cache", ".ruff_cache", "node_modules"})
EXCLUDED_SUFFIXES = frozenset({".pyc", ".pyo"})
def iter_deploy_files(root: Path = PROJECT_ROOT) -> list[Path]:
"""Alle Dateien des Upload-Pakets, sortiert (für reproduzierbare Archive)."""
files = [root / name for name in INCLUDE_FILES if (root / name).is_file()]
for directory in INCLUDE_DIRS:
for path in (root / directory).rglob("*"):
relative_parts = path.relative_to(root).parts
if not path.is_file() or EXCLUDED_DIR_NAMES.intersection(relative_parts):
continue
if path.suffix in EXCLUDED_SUFFIXES:
continue
files.append(path)
return sorted(files)
def build_zip(target: Path, root: Path = PROJECT_ROOT) -> list[str]:
"""Schreibt das ZIP und gibt die enthaltenen Pfade (mit ``/``) zurück."""
target.parent.mkdir(parents=True, exist_ok=True)
names = []
with zipfile.ZipFile(target, "w", compression=zipfile.ZIP_DEFLATED) as archive:
for path in iter_deploy_files(root):
# as_posix(): immer „/“ als Trenner, unabhängig vom Betriebssystem.
name = path.relative_to(root).as_posix()
archive.write(path, name)
names.append(name)
return names
def main(argv: list[str] | None = None) -> int:
"""Kommandozeilen-Einstieg."""
parser = argparse.ArgumentParser(description="Upload-Paket für cPanel bauen.")
parser.add_argument("-o", "--output", type=Path, default=DEFAULT_OUTPUT, help="Ziel-ZIP")
args = parser.parse_args(argv)
names = build_zip(args.output)
size_kb = args.output.stat().st_size / 1024
print(f"{args.output} erstellt: {len(names)} Dateien, {size_kb:.0f} KB")
return 0
if __name__ == "__main__":
sys.exit(main())