From 3596ce80d4057984935e9222a353a54b2f5fad9b Mon Sep 17 00:00:00 2001 From: Matthias Jung Date: Sun, 22 Feb 2026 23:49:45 +0100 Subject: [PATCH 1/2] feature: create zip, fixes #9 --- main.py | 5 +++++ src/build.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/main.py b/main.py index a53294a..ef5b04b 100644 --- a/main.py +++ b/main.py @@ -23,6 +23,7 @@ def main( output: Annotated[str | None, typer.Option("--output", "-o", help="Destination directory to build to.")] = None, render_editions: Annotated[bool, typer.Option(help="Skip building editions.")] = True, render_solutions: Annotated[bool, typer.Option(help="Skip building solutions.")] = True, + build_zip: Annotated[bool, typer.Option(help="Whether to build a zip file of the output.")] = False, ) -> None: conf = config.Config(content_path=input, build_path=output) bd = build.Build(conf) @@ -42,6 +43,10 @@ def main( # Copy assets to build folder bd.build_assets() + if build_zip: + # Build zip file of output + bd.build_zip() + if __name__ == "__main__": app() diff --git a/src/build.py b/src/build.py index 6d56a6f..64f71d4 100644 --- a/src/build.py +++ b/src/build.py @@ -1,7 +1,9 @@ import json +import os import random import re import shutil +import zipfile from pathlib import Path from jinja2 import Environment, FileSystemLoader @@ -515,3 +517,39 @@ class Build: page = solution_template.render(question=question, solution=solution, number=solution_file.stem) page = self.__build_page(page, course_wrapper=False) file.write(page) + + def build_zip(self, zip_name: str | None = None) -> Path: + """Create a zip archive of the complete build output directory. + + The zip file is written into the build directory itself and is excluded + from the archive contents. + """ + + build_dir = self.config.p_build + build_dir.mkdir(parents=True, exist_ok=True) + + zip_path = build_dir / (zip_name if zip_name is not None else f"{build_dir.name}.zip") + zip_path = zip_path.resolve() + + # If a previous archive exists, remove it first to avoid zipping stale data. + if zip_path.exists(): + zip_path.unlink() + + tqdm.write(f"Creating zip archive: {zip_path}") + + with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as zf: + # os.walk includes dotfiles; sort for stable archives. + for root, dirs, files in os.walk(build_dir): + dirs.sort() + files.sort() + + root_path = Path(root) + for filename in files: + file_path = (root_path / filename).resolve() + if file_path == zip_path: + continue + + arcname = file_path.relative_to(build_dir.resolve()) + zf.write(file_path, arcname=arcname) + + return zip_path From da3cfade6a056658fa87d8fe9806bf32b88718d8 Mon Sep 17 00:00:00 2001 From: Matthias Jung Date: Mon, 23 Feb 2026 00:10:42 +0100 Subject: [PATCH 2/2] fix: add zip also to build.py --- build.py | 1 + 1 file changed, 1 insertion(+) diff --git a/build.py b/build.py index 5caae9f..7646086 100644 --- a/build.py +++ b/build.py @@ -14,3 +14,4 @@ bd.build_edition("EA") bd.build_edition("NEA") bd.build_assets() bd.build_solutions() +bd.build_zip()