"""Publish the Space + sync the public GitHub mirror in one command. Run from `chief-engineer/`: make publish or uv run python -m scripts.publish What it does: 1. Runs `scripts.deploy_preflight --push` to update the HF Space and reboot. 2. Syncs the Space-facing file subset to the public mirror repo `kylebrodeur/microfactory-node` and pushes `main`. Environment: - HF_TOKEN or `hf auth login` must be active for the Space push. - `gh auth login` or SSH key must be active for the GitHub push. """ from __future__ import annotations import argparse import os import shutil import subprocess import sys import tempfile from pathlib import Path ROOT = Path(__file__).resolve().parent.parent MIRROR_REPO = "git@github.com:kylebrodeur/microfactory-node.git" MIRROR_NAME = "microfactory-node" # Same rules as deploy_preflight.SPACE_IGNORE, plus a .gitignore for the mirror itself. MIRROR_IGNORE = [ "docs/**", "!docs/reference/DEPLOYMENT.md", "!docs/reference/SIMULATION.md", "spike/**", "field_logs/**", "deliberation_logs/**", ".venv/**", "node_modules/**", "recordings/**", "**/__pycache__/**", "**/*.pyc", ".git/**", ".gitignore", ".env", ".agents/**", ".codeboarding/**", ".codeboardingignore", "data/policy.json", "data/_generated.glb", "data/_vprint.gif", "uv.lock", ".pytest_cache/**", "*.cap/**", "learn/finetune/REPORT.md", "learn/finetune/REPORT_v1.md", ] def match_ignore(rel_path: str) -> bool: import fnmatch # (pattern, is_negative) patterns: list[tuple[str, bool]] = [] for pat in MIRROR_IGNORE: is_neg = pat.startswith("!") real_pat = pat[1:] if is_neg else pat patterns.append((real_pat, is_neg)) # Inclusions override exclusions. for pat, is_neg in patterns: if is_neg and _matches(rel_path, pat): return False for pat, is_neg in patterns: if not is_neg and _matches(rel_path, pat): return True return False def _matches(rel_path: str, pat: str) -> bool: import fnmatch if pat.endswith("/**"): prefix = pat[:-3] return rel_path.startswith(prefix + "/") or rel_path == prefix if pat.startswith("**/"): suffix = pat[3:] return rel_path.endswith(suffix) or ("/" + suffix) in rel_path if pat.startswith("*"): return fnmatch.fnmatch(rel_path, pat) or fnmatch.fnmatch(Path(rel_path).name, pat) return rel_path == pat or rel_path.startswith(pat + "/") def run(cmd: list[str], cwd: Path | None = None, check: bool = True, capture_output: bool = False) -> subprocess.CompletedProcess: print(f" $ {' '.join(cmd)}") kwargs = {"cwd": cwd, "check": check, "text": True} if capture_output: kwargs["capture_output"] = True return subprocess.run(cmd, **kwargs) def publish_space(*, no_reboot: bool = False, dry_run: bool = False) -> bool: print("\nā« Step 1 — update the HF Space") cmd = ["uv", "run", "python", "-m", "scripts.deploy_preflight"] if not dry_run: cmd.append("--push") if no_reboot: cmd.append("--no-reboot") r = subprocess.run(cmd, cwd=ROOT, text=True) return r.returncode == 0 def sync_mirror(*, dry_run: bool = False) -> bool: print("\nšŸŖž Step 2 — sync public GitHub mirror") mirror_dir = Path(tempfile.gettempdir()) / f"{MIRROR_NAME}-sync" if mirror_dir.exists(): shutil.rmtree(mirror_dir) mirror_dir.mkdir(parents=True) print(f" Mirroring Space-facing subset → {mirror_dir}") files_copied = 0 for src in sorted(ROOT.rglob("*")): if not src.is_file(): continue rel = src.relative_to(ROOT).as_posix() if match_ignore(rel): continue dst = mirror_dir / rel dst.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(src, dst) files_copied += 1 # Drop transient pycache/pi that may have slipped through for d in list(mirror_dir.rglob("__pycache__")): shutil.rmtree(d) pi_dir = mirror_dir / ".pi" if pi_dir.exists(): shutil.rmtree(pi_dir) print(f" Copied {files_copied} files") run(["git", "init", "-b", "main"], cwd=mirror_dir, check=False) run(["git", "remote", "add", "origin", MIRROR_REPO], cwd=mirror_dir, check=False) run(["git", "fetch", "origin", "main"], cwd=mirror_dir, check=False) run(["git", "reset", "--hard", "origin/main"], cwd=mirror_dir, check=False) # Clean working tree but keep .git for item in mirror_dir.iterdir(): if item.name == ".git": continue if item.is_dir(): shutil.rmtree(item) else: item.unlink() # Re-copy after reset for src in sorted(ROOT.rglob("*")): if not src.is_file(): continue rel = src.relative_to(ROOT).as_posix() if match_ignore(rel): continue dst = mirror_dir / rel dst.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(src, dst) for d in list(mirror_dir.rglob("__pycache__")): shutil.rmtree(d) if (mirror_dir / ".pi").exists(): shutil.rmtree(mirror_dir / ".pi") run(["git", "add", "-A"], cwd=mirror_dir) status = run(["git", "status", "--short"], cwd=mirror_dir, check=True, capture_output=True) if not status.stdout.strip(): print(" Mirror already up to date.") return True print(f" Changes:\n{status.stdout}") if dry_run: print(" (dry-run: not committing or pushing)") return True run(["git", "commit", "-m", "Re-sync from private microfactory-lab"], cwd=mirror_dir) run(["git", "push", "origin", "main"], cwd=mirror_dir) print(f" āœ… Mirror pushed: https://github.com/kylebrodeur/{MIRROR_NAME}") return True def main() -> int: ap = argparse.ArgumentParser(description="Publish the HF Space + sync the public GitHub mirror.") ap.add_argument("--no-reboot", action="store_true", help="push to Space but skip factory reboot") ap.add_argument("--mirror-only", action="store_true", help="skip the HF Space push; only sync the mirror") ap.add_argument("--dry-run", action="store_true", help="show what would happen; do not push") args = ap.parse_args() print("Publish Microfactory Node: HF Space + public GitHub mirror") print("=" * 60) if not args.mirror_only: if not publish_space(no_reboot=args.no_reboot, dry_run=args.dry_run): print("\nšŸ”“ Space push failed — mirror sync skipped.") return 1 if not sync_mirror(dry_run=args.dry_run): print("\nšŸ”“ Mirror sync failed.") return 1 print("\n🟢 Published: Space updated, mirror synced.") return 0 if __name__ == "__main__": sys.exit(main())