feat: full pi config in containers — git: packages vendored at bake (ignore-scripts), npm shim for runtime installs, dotfiles mount + POST /api/pi-config/resync rebuilds worker+ops images (95% cover, race clean)
This commit is contained in:
+38
-17
@@ -4,7 +4,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
SRC="${LVMH_PI_AGENT_DIR:-$HOME/.dotfiles/pi/agent}"
|
||||
DEST="$(dirname "$0")/../docker/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).
|
||||
@@ -31,20 +31,41 @@ rsync -a --delete \
|
||||
--exclude '.pi/' \
|
||||
"$SRC/" "$DEST/"
|
||||
|
||||
# Container sessions cannot run git: package postinstalls (e.g. husky) — keep
|
||||
# registry (npm:) packages only in the baked settings.json.
|
||||
python3 - "$DEST/settings.json" <<'PY'
|
||||
import json, sys
|
||||
p = sys.argv[1]
|
||||
with open(p) as f:
|
||||
s = json.load(f)
|
||||
pkgs = s.get("packages") or []
|
||||
kept = [x for x in pkgs if x.startswith("npm:")]
|
||||
if pkgs != kept:
|
||||
s["packages"] = kept
|
||||
with open(p, "w") as f:
|
||||
json.dump(s, f, indent=2)
|
||||
f.write("\n")
|
||||
print(f"rsync-pi-agent: dropped {len(pkgs) - len(kept)} git: packages from baked settings")
|
||||
# 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, 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)
|
||||
shutil.rmtree(target / ".git", ignore_errors=True)
|
||||
if (target / "package.json").exists():
|
||||
npm = "/usr/bin/npm" if Path("/usr/bin/npm").exists() else "npm"
|
||||
subprocess.run(
|
||||
[npm, "install", "--omit=dev", "--ignore-scripts", "--no-audit", "--no-fund"],
|
||||
cwd=target, check=True, capture_output=True,
|
||||
)
|
||||
PY
|
||||
echo "rsync-pi-agent: synced $(find "$DEST" -type f | wc -l) files from $SRC"
|
||||
|
||||
echo "rsync-pi-agent: synced $(find "$DEST" -type f | wc -l) files from $SRC (git: packages preserved)"
|
||||
|
||||
Reference in New Issue
Block a user