mirror of
https://github.com/basnijholt/compose-farm.git
synced 2026-02-03 14:13:26 +00:00
135 lines
4.3 KiB
Python
Executable File
135 lines
4.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Record CLI demos using VHS."""
|
|
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from rich.console import Console
|
|
|
|
from compose_farm.config import load_config
|
|
from compose_farm.state import load_state
|
|
|
|
console = Console()
|
|
SCRIPT_DIR = Path(__file__).parent
|
|
STACKS_DIR = Path("/opt/stacks")
|
|
CONFIG_FILE = STACKS_DIR / "compose-farm.yaml"
|
|
OUTPUT_DIR = SCRIPT_DIR.parent.parent / "assets"
|
|
|
|
DEMOS = ["install", "quickstart", "logs", "compose", "update", "migration", "apply"]
|
|
|
|
|
|
def _run(cmd: list[str], **kw) -> bool:
|
|
return subprocess.run(cmd, check=False, **kw).returncode == 0
|
|
|
|
|
|
def _set_config(host: str) -> None:
|
|
"""Set audiobookshelf host in config file."""
|
|
_run(["sed", "-i", f"s/audiobookshelf: .*/audiobookshelf: {host}/", str(CONFIG_FILE)])
|
|
|
|
|
|
def _get_hosts() -> tuple[str | None, str | None]:
|
|
"""Return (config_host, state_host) for audiobookshelf."""
|
|
config = load_config()
|
|
state = load_state(config)
|
|
return config.stacks.get("audiobookshelf"), state.get("audiobookshelf")
|
|
|
|
|
|
def _setup_state(demo: str) -> bool:
|
|
"""Set up required state for demo. Returns False on failure."""
|
|
if demo not in ("migration", "apply"):
|
|
return True
|
|
|
|
config_host, state_host = _get_hosts()
|
|
|
|
if demo == "migration":
|
|
# Migration needs audiobookshelf on nas in BOTH config and state
|
|
if config_host != "nas":
|
|
console.print("[yellow]Setting up: config → nas[/yellow]")
|
|
_set_config("nas")
|
|
if state_host != "nas":
|
|
console.print("[yellow]Setting up: state → nas[/yellow]")
|
|
if not _run(["cf", "apply"], cwd=STACKS_DIR):
|
|
return False
|
|
|
|
elif demo == "apply":
|
|
# Apply needs config=nas, state=anton (so there's something to apply)
|
|
if config_host != "nas":
|
|
console.print("[yellow]Setting up: config → nas[/yellow]")
|
|
_set_config("nas")
|
|
if state_host == "nas":
|
|
console.print("[yellow]Setting up: state → anton[/yellow]")
|
|
_set_config("anton")
|
|
if not _run(["cf", "apply"], cwd=STACKS_DIR):
|
|
return False
|
|
_set_config("nas")
|
|
|
|
return True
|
|
|
|
|
|
def _record(name: str, index: int, total: int) -> bool:
|
|
"""Record a single demo."""
|
|
console.print(f"[cyan][{index}/{total}][/cyan] [green]Recording:[/green] {name}")
|
|
if _run(["vhs", str(SCRIPT_DIR / f"{name}.tape")], cwd=STACKS_DIR):
|
|
console.print("[green] ✓ Done[/green]")
|
|
return True
|
|
console.print("[red] ✗ Failed[/red]")
|
|
return False
|
|
|
|
|
|
def _reset_after(demo: str, next_demo: str | None) -> None:
|
|
"""Reset state after demos that modify audiobookshelf."""
|
|
if demo not in ("quickstart", "migration"):
|
|
return
|
|
_set_config("nas")
|
|
if next_demo != "apply": # Let apply demo show the migration
|
|
_run(["cf", "apply"], cwd=STACKS_DIR)
|
|
|
|
|
|
def _restore_config(original: str) -> None:
|
|
"""Restore original config and sync state."""
|
|
console.print("[yellow]Restoring original config...[/yellow]")
|
|
CONFIG_FILE.write_text(original)
|
|
_run(["cf", "apply"], cwd=STACKS_DIR)
|
|
|
|
|
|
def _main() -> int:
|
|
if not shutil.which("vhs"):
|
|
console.print("[red]VHS not found. Install: brew install vhs[/red]")
|
|
return 1
|
|
|
|
if not _run(["git", "-C", str(STACKS_DIR), "diff", "--quiet", "compose-farm.yaml"]):
|
|
console.print("[red]compose-farm.yaml has uncommitted changes[/red]")
|
|
return 1
|
|
|
|
demos = [d for d in sys.argv[1:] if d in DEMOS] or DEMOS
|
|
if sys.argv[1:] and not demos:
|
|
console.print(f"[red]Unknown demo. Available: {', '.join(DEMOS)}[/red]")
|
|
return 1
|
|
|
|
# Save original config to restore after recording
|
|
original_config = CONFIG_FILE.read_text()
|
|
|
|
try:
|
|
for i, demo in enumerate(demos, 1):
|
|
if not _setup_state(demo):
|
|
return 1
|
|
if not _record(demo, i, len(demos)):
|
|
return 1
|
|
_reset_after(demo, demos[i] if i < len(demos) else None)
|
|
finally:
|
|
_restore_config(original_config)
|
|
|
|
# Move outputs
|
|
OUTPUT_DIR.mkdir(exist_ok=True)
|
|
for f in (STACKS_DIR / "docs/assets").glob("*.[gw]*"):
|
|
shutil.move(str(f), str(OUTPUT_DIR / f.name))
|
|
|
|
console.print(f"\n[green]Done![/green] Saved to {OUTPUT_DIR}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(_main())
|