168 lines
5.1 KiB
Python
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()) |