Files
Assetmanager/tools/create_github_source.py

98 lines
3.3 KiB
Python
Executable File

#!/usr/bin/env python3
"""Create a sanitized GitHub source ZIP with one assetmanager/ root folder."""
from __future__ import annotations
import argparse
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile
ROOT = Path(__file__).resolve().parents[1]
EXCLUDED_NAMES = {
".git", ".venv", "venv", "__pycache__", ".pytest_cache", ".mypy_cache",
".ruff_cache", "dist", "THIRD_PARTY_LICENSES",
}
EXCLUDED_FILES = {
".env",
"data/config/config.json",
"data/config/APPINFO.json",
"app/main_old.py",
"PRUEFEN-0.5.5.5.sh",
"assetmanager.db",
".public-release-local-patterns",
}
RUNTIME_DIRS = {
"data/postgres", "data/uploads", "data/logs", "data/backups",
"data/backup", "data/scripts", "app/static/uploads",
}
def should_exclude(relative: Path) -> bool:
posix = relative.as_posix()
if any(part in EXCLUDED_NAMES for part in relative.parts):
return True
if posix in EXCLUDED_FILES:
return True
if relative.name.startswith(".env") and relative.name != ".env.example":
return True
if relative.suffix.lower() in {".db", ".sqlite", ".sqlite3", ".pyc", ".zip"}:
return True
return any(posix == directory or posix.startswith(directory + "/") for directory in RUNTIME_DIRS)
def copy_source(destination: Path) -> None:
for source in ROOT.rglob("*"):
relative = source.relative_to(ROOT)
if should_exclude(relative):
continue
target = destination / relative
if source.is_dir():
target.mkdir(parents=True, exist_ok=True)
elif source.is_file():
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, target)
for directory in sorted(RUNTIME_DIRS):
marker = destination / directory / ".gitkeep"
marker.parent.mkdir(parents=True, exist_ok=True)
marker.touch()
def make_zip(source_root: Path, output: Path) -> None:
output.parent.mkdir(parents=True, exist_ok=True)
with ZipFile(output, "w", compression=ZIP_DEFLATED, compresslevel=9) as archive:
for path in sorted(source_root.rglob("*")):
if path.is_file():
archive.write(path, Path("assetmanager") / path.relative_to(source_root))
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--output", type=Path)
args = parser.parse_args()
version = (ROOT / "VERSION").read_text(encoding="utf-8").strip()
output = (args.output or ROOT / "dist" / f"assetmanager-github-{version}.zip").resolve()
with tempfile.TemporaryDirectory(prefix="assetmanager-public-") as temp_name:
staged = Path(temp_name) / "assetmanager"
staged.mkdir()
copy_source(staged)
checker = staged / "tools" / "check_public_release.py"
command = [sys.executable, str(checker), "--root", str(staged)]
denylist = ROOT / ".public-release-local-patterns"
if denylist.is_file():
command.extend(["--denylist", str(denylist)])
result = subprocess.run(command)
if result.returncode:
print("GitHub ZIP was not created because the staged source failed validation.", file=sys.stderr)
return result.returncode
make_zip(staged, output)
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())