Files
malkuthro-ComfyUI-Koolook/scripts/sync_to_dev.py
T
malkuthroandClaude Opus 4.7 0a914cb8de chore(dev): add scripts/sync_to_dev.py + KOLOOK_COMFYUI_DEV_PATH env var (#40)
For one-line fixes that don't deserve their own version bump, copy the
runtime files into the maintainer's live ComfyUI custom_nodes folder
directly instead of going through the publish-and-update round-trip.

What this adds:
- `scripts/sync_to_dev.py` — minimal Python (no third-party deps); reads
  `KOLOOK_COMFYUI_DEV_PATH` from the environment (auto-loads `.env` at
  the repo root); copies the curated set of runtime-relevant files
  (top-level `k_*.py`, `__init__.py`, `config.json`, `forks/`, `web/`)
  while skipping CI/docs/metadata. Errors out cleanly when the env var
  is unset — never guesses a path.
- `.env.example` entry `KOLOOK_COMFYUI_DEV_PATH=` with explanatory
  comment, matching the existing `KOLOOK_*` convention.
- `CLAUDE.md` section documenting the trigger phrase `dev-sync` so a
  fresh agent session knows to invoke the script when the maintainer
  asks for it.

This is a development-loop convenience only — anything worth shipping
still goes through the normal PR + release flow.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 00:02:02 +02:00

138 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""
Copy runtime-relevant Koolook files into a live ComfyUI custom_nodes
folder so a fix can be tested without a tag-and-publish round-trip.
The target path is read from the KOLOOK_COMFYUI_DEV_PATH environment
variable (loaded from `.env` at the repo root if present). The variable
is intentionally kept out of the committed tree — see `.env.example`.
Usage:
python scripts/sync_to_dev.py # copy all runtime files
python scripts/sync_to_dev.py --dry-run # show what would copy
Exit codes:
0 success
2 KOLOOK_COMFYUI_DEV_PATH unset or target missing
This script never reaches outside the repo, never deletes anything in
the source, and only touches paths under the configured target. It does
overwrite files in the target — that's the point.
"""
from __future__ import annotations
import argparse
import os
import shutil
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
# Files / dirs ComfyUI actually loads at runtime. Anything outside this
# list (CI, docs, .claude/, .github/, .cursor/, CHANGELOG, LICENSE,
# pyproject.toml, README, fork manifest YAML, etc.) does not affect what
# ComfyUI executes and is intentionally skipped.
RUNTIME_PATHS: tuple[str, ...] = (
"__init__.py",
"config.json",
"k_ai_pipeline.py",
"k_easy_image_batch.py",
"k_easy_resize.py",
"k_easy_track.py",
"k_easy_version.py",
"k_easy_wan22_prompt.py",
"forks",
"web",
)
# When copying directories, exclude these subpaths — they are dev-only
# metadata that ComfyUI doesn't need and which can churn unnecessarily.
DIR_EXCLUDES: tuple[str, ...] = (
"__pycache__",
"UPSTREAM_PIN.yaml",
"THIRD_PARTY.md",
"forks_manifest.yaml",
"README.md",
)
def load_dotenv(env_path: Path) -> None:
"""Minimal `.env` loader. No dependency on python-dotenv."""
if not env_path.exists():
return
for line in env_path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, _, value = line.partition("=")
# Don't overwrite anything already in the environment.
os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
def _ignore(_dir: str, names: list[str]) -> list[str]:
return [n for n in names if n in DIR_EXCLUDES]
def sync(target: Path, dry_run: bool) -> int:
copied = 0
for rel in RUNTIME_PATHS:
src = REPO_ROOT / rel
if not src.exists():
continue
dst = target / rel
if dry_run:
print(f"would copy: {rel}")
copied += 1
continue
if src.is_dir():
if dst.exists():
shutil.rmtree(dst)
shutil.copytree(src, dst, ignore=_ignore)
else:
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src, dst)
print(f"copied: {rel}")
copied += 1
return copied
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--dry-run",
action="store_true",
help="Print what would be copied without touching the target.",
)
args = parser.parse_args()
load_dotenv(REPO_ROOT / ".env")
target_str = os.environ.get("KOLOOK_COMFYUI_DEV_PATH")
if not target_str:
print(
"KOLOOK_COMFYUI_DEV_PATH not set. Add it to .env "
"(see .env.example).",
file=sys.stderr,
)
return 2
target = Path(target_str).expanduser()
if not target.exists():
print(f"target does not exist: {target}", file=sys.stderr)
return 2
if not target.is_dir():
print(f"target is not a directory: {target}", file=sys.stderr)
return 2
n = sync(target, dry_run=args.dry_run)
verb = "would sync" if args.dry_run else "synced"
print(f"\n{verb} {n} entries -> {target}")
if not args.dry_run:
print("restart ComfyUI to pick up changes.")
return 0
if __name__ == "__main__":
sys.exit(main())