Files
lvmh/deploy/rsync-pi-agent.sh

81 lines
3.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Sync the user's pi config (from dotfiles) into docker/pi-agent/ so the
# worker image bakes it. Filtered: no secrets, no machine-local state.
set -euo pipefail
SRC="${LVMH_PI_AGENT_DIR:-$HOME/.dotfiles/pi/agent}"
DEST="${LVMH_PI_BAKE_DIR:-$(dirname "$0")/../docker/pi-agent}"
# DEST must exist even without dotfiles: the worker Dockerfile COPYs it
# unconditionally (a missing dir would fail the build).
mkdir -p "$DEST"
if [ ! -f "$SRC/settings.json" ]; then
echo "rsync-pi-agent: $SRC/settings.json not found — skipping dotfiles sync" >&2
exit 0
fi
rsync -a --delete \
--exclude 'auth.json' \
--exclude 'models-store.json' \
--exclude 'sessions/' \
--exclude 'cache/' \
--exclude 'npm/' \
--exclude 'git/' \
--exclude 'trust.json' \
--exclude 'mcp-*' \
--exclude 'run-history*' \
--exclude 'vibes' \
--exclude '*.log' \
--exclude 'scratch/' \
--exclude '.pi/' \
"$SRC/" "$DEST/"
# Pre-seed the git: package cache: clone each pinned git: package into
# docker/pi-agent/git/... where pi's package manager installs them, with
# lifecycle scripts ignored (postinstalls like husky crash headless).
# Keeps subagents/todo/async tooling present with no runtime network need.
# Requires network access to github.com at bake time.
python3 - "$DEST" <<'PY'
import json, os, re, shutil, subprocess, sys
from pathlib import Path
dest = Path(sys.argv[1])
settings = json.loads((dest / "settings.json").read_text())
git_root = dest / "git"
if git_root.exists():
shutil.rmtree(git_root)
for pkg in settings.get("packages") or []:
if not pkg.startswith("git:"):
continue
rest = pkg[4:]
m = re.match(r"^(.+)@([0-9a-f]{7,40})$", rest)
repo, ref = (m.group(1), m.group(2)) if m else (rest, None)
target = git_root / repo
target.parent.mkdir(parents=True, exist_ok=True)
url = f"https://{repo}" if not repo.startswith("https://") else repo
print(f"rsync-pi-agent: cloning {repo}@{ref or 'HEAD'}")
subprocess.run(["git", "clone", "--quiet", url, str(target)], check=True)
if ref:
subprocess.run(["git", "checkout", "--quiet", ref], cwd=target, check=True)
# keep .git: pi's package manager runs `git fetch` + `rev-parse` on
# existing installs; a stripped dir would fail resolution.
if (target / "package.json").exists():
npm = "/usr/bin/npm" if Path("/usr/bin/npm").exists() else "npm"
# --ignore-scripts: prepare scripts (husky) are devDependency-based
# and fail headless; extensions are plain TS needing runtime deps only.
subprocess.run(
[npm, "install", "--omit=dev", "--ignore-scripts", "--no-audit", "--no-fund"],
cwd=target, check=True, capture_output=True,
)
PY
# Append the lvmh deployment guide to the baked APPEND_SYSTEM.md so every
# spawned agent knows how to deploy hosts / push / open PRs.
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if [ -f "$SCRIPT_DIR/../docker/agent-append-system.md" ]; then
printf '\n%s\n' "$(cat "$SCRIPT_DIR/../docker/agent-append-system.md")" >> "$DEST/APPEND_SYSTEM.md"
fi
echo "rsync-pi-agent: synced $(find "$DEST" -type f | wc -l) files from $SRC (git: packages preserved)"