Files
ScientificManuscriptTemplate/resources/scripts/task_utils.py
T
2026-06-19 17:31:51 +02:00

168 lines
5.1 KiB
Python

#!/usr/bin/env python3
"""
task_utils.py — cross-platform file operations for the Taskfile pipeline.
Replaces shell-specific commands (PowerShell Compress-Archive, Remove-Item,
etc.) with plain Python stdlib calls that behave identically on Windows,
Linux, and macOS. Called from Task as:
python ../resources/scripts/task_utils.py <subcommand> [args...]
Subcommands:
zip SRC_DIR DEST_ZIP
Zip the contents of SRC_DIR into DEST_ZIP (overwrites if it exists).
Fails with a clear message (exit 1) if SRC_DIR doesn't exist.
zip-if-exists SRC_DIR DEST_ZIP
Same as `zip`, but exits 0 with a warning (no error) if SRC_DIR
doesn't exist, instead of failing. Used for optional things like
a project's data/ folder.
clean-project PROJECT_DIR
Remove _output/, .quarto/, and any *_files/*_cache directories
found anywhere under PROJECT_DIR. Safe to call even if nothing
exists yet.
copy-if-exists SRC DEST
Copy a single file from SRC to DEST if SRC exists; otherwise
print a warning and exit 0 (does not fail the pipeline).
today
Print today's date as YYYY-MM-DD (used for the finalized/ folder
name). No platform-specific date command needed.
"""
import shutil
import sys
import zipfile
from datetime import date
from pathlib import Path
def cmd_zip(src_dir: str, dest_zip: str, allow_missing: bool) -> int:
src = Path(src_dir)
dest = Path(dest_zip)
if not src.exists() or not src.is_dir():
msg = f"'{src}' does not exist or is not a directory"
if allow_missing:
print(f"WARNING: {msg} — skipping zip of {dest}")
return 0
print(f"ERROR: {msg} — did you render first?", file=sys.stderr)
return 1
files = [p for p in src.rglob("*") if p.is_file()]
if not files:
msg = f"'{src}' exists but contains no files"
if allow_missing:
print(f"WARNING: {msg} — skipping zip of {dest}")
return 0
print(f"ERROR: {msg}", file=sys.stderr)
return 1
dest.parent.mkdir(parents=True, exist_ok=True)
if dest.exists():
dest.unlink()
with zipfile.ZipFile(dest, "w", zipfile.ZIP_DEFLATED) as zf:
for f in files:
zf.write(f, f.relative_to(src))
print(f"Created {dest} ({len(files)} files)")
return 0
def cmd_clean_project(project_dir: str) -> int:
root = Path(project_dir)
for name in ("_output", ".quarto"):
target = root / name
if target.exists():
shutil.rmtree(target, ignore_errors=True)
print(f"Removed {target}")
for pattern in ("*_files", "*_cache"):
for match in root.rglob(pattern):
if match.is_dir():
shutil.rmtree(match, ignore_errors=True)
print(f"Removed {match}")
return 0
def cmd_clean_zips(project_dir: str) -> int:
root = Path(project_dir)
for match in root.rglob("*.zip"):
if match.is_file():
shutil.rmtree(match, ignore_errors=True)
print(f"Removed {match}")
return 0
def cmd_copy_if_exists(src: str, dest: str) -> int:
src_path = Path(src)
dest_path = Path(dest)
if not src_path.exists():
print(f"WARNING: '{src_path}' not found, skipping copy")
return 0
dest_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src_path, dest_path)
print(f"Copied {src_path} -> {dest_path}")
return 0
def cmd_today() -> int:
print(date.today().isoformat())
return 0
def main() -> int:
if len(sys.argv) < 2:
print(__doc__, file=sys.stderr)
return 1
subcommand = sys.argv[1]
args = sys.argv[2:]
if subcommand == "zip":
if len(args) != 2:
print("usage: task_utils.py zip SRC_DIR DEST_ZIP", file=sys.stderr)
return 1
return cmd_zip(args[0], args[1], allow_missing=False)
if subcommand == "zip-if-exists":
if len(args) != 2:
print("usage: task_utils.py zip-if-exists SRC_DIR DEST_ZIP", file=sys.stderr)
return 1
return cmd_zip(args[0], args[1], allow_missing=True)
if subcommand == "clean-project":
if len(args) != 1:
print("usage: task_utils.py clean-project PROJECT_DIR", file=sys.stderr)
return 1
return cmd_clean_project(args[0])
if subcommand == "clean-zips":
if len(args) != 1:
print("usage: task_utils.py clean-zips PROJECT_DIR", file=sys.stderr)
return 1
return cmd_clean_zips(args[0])
if subcommand == "copy-if-exists":
if len(args) != 2:
print("usage: task_utils.py copy-if-exists SRC DEST", file=sys.stderr)
return 1
return cmd_copy_if_exists(args[0], args[1])
if subcommand == "today":
return cmd_today()
print(f"ERROR: unknown subcommand '{subcommand}'", file=sys.stderr)
print(__doc__, file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main())