feat(deploy): sync a scenario's backend/ into the live workspace root

deploy-scenario.sh now single-sources the application backend: it copies the git-tracked files under [scenario].backend into the workspace root (where .pi/ sits beside them), preserving modes and skipping caches/venvs. It overwrites but never prunes, and never restarts -- it prints a restart reminder when backend files change. README's Application code section updated to match.
This commit is contained in:
Kai
2026-08-30 21:48:25 -07:00
parent a337ea978f
commit fb1a1d2b10
2 changed files with 44 additions and 5 deletions
+6 -5
View File
@@ -65,11 +65,12 @@ A scenario that runs its own service keeps that service's source under
units and config templates. It is the single source of truth; the live checkout
at the scenario's `workspace` path is a runtime copy.
`deploy-scenario.sh` installs only `workspace/` (the `.pi` config) and renders the
launch contract; it does not touch `backend/`, build a venv or restart a service.
Rolling application code out to the live checkout and restarting the unit stays an
operator step, deliberately: restarting decides when to interrupt a live
conversation.
`deploy-scenario.sh` installs `workspace/` (the `.pi` config), vendors shared
extensions, renders the launch contract, and syncs `backend/` into the workspace
root — copying only git-tracked files (build caches and virtualenvs never leak)
and preserving file modes. It never prunes files the repo no longer tracks, never
builds a venv, and never restarts the service: it prints a reminder instead, since
restarting decides when to interrupt a live conversation.
## Start here
+38
View File
@@ -108,6 +108,44 @@ if [ "$APPLY" -eq 1 ] && [ -d "$DIR/workspace/bin" ]; then
done < <(cd "$DIR/workspace" && find bin -type f -printf '%P\n' 2>/dev/null | sed 's|^|bin/|')
fi
# ---------------------------------------------------------------------------
# 1c. Application backend
#
# A scenario that owns a service keeps its source under backend/ (set by
# [scenario].backend). It installs into the workspace root, where .pi/ sits
# beside it, so the repository is the single source of truth. Only git-tracked
# files are copied -- build caches and virtualenvs never leak -- and file modes
# (notably the execute bit) are preserved. This overwrites but never prunes:
# removing a file the repo no longer tracks from a live tree is too blunt to do
# unattended. Like every other step it never restarts the service; it prints a
# reminder, because restarting decides when to interrupt a live conversation.
# ---------------------------------------------------------------------------
BACKEND="$(toml_get "$PROFILE" scenario backend)"
if [ -n "$BACKEND" ]; then
case "$BACKEND" in /*) ;; *) BACKEND="$REPO_ROOT/$BACKEND" ;; esac
[ -d "$BACKEND" ] || die "profile sets [scenario].backend to '$BACKEND', which does not exist"
BACKEND_CHANGES=0
while IFS= read -r rel; do
src="$BACKEND/$rel"
dst="$WORKSPACE/$rel"
if [ -f "$dst" ] && cmp -s "$src" "$dst"; then
continue
fi
BACKEND_CHANGES=$((BACKEND_CHANGES + 1))
if [ -f "$dst" ]; then info " update $rel"; else info " create $rel"; fi
if [ "$APPLY" -eq 1 ]; then
install -d -m 755 "$(dirname "$dst")"
install -m "$(stat -c '%a' "$src")" "$src" "$dst"
fi
done < <(git -C "$BACKEND" ls-files)
CHANGES=$((CHANGES + BACKEND_CHANGES))
if [ "$BACKEND_CHANGES" -gt 0 ]; then
warn "backend: $BACKEND_CHANGES file(s) changed; restart $(toml_get "$PROFILE" scenario service) to apply"
else
ok "backend already matches"
fi
fi
[ "$CHANGES" -eq 0 ] && ok "workspace already matches"
# ---------------------------------------------------------------------------