98 lines
3.3 KiB
Python
Executable File
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())
|