ai-stack: chown the stack dir back to ACTUAL_USER after every installer run

local-ai-setup.sh runs as whoever invoked this wrapper — root, since
setup.sh itself runs under sudo — so every file it generates
(docker-compose.yml, .env, requirements.txt, server.py, mcp_server.py,
pull-models.sh, start/stop/status.sh) came out root-owned. Nothing handed
that back to ACTUAL_USER unconditionally: the only existing
ensure_docker_dir_ownership call was inside the cloud-provider wiring
block, so it silently never ran at all for anyone who skipped cloud
providers.

Confirmed live: this repo's own "Skipped. Run later: cd $AS_DIR && bash
local-ai-setup.sh" message tells the user to re-run it directly later as
themselves (no sudo) — which then fails with "Permission denied" on any
file root created during the original sudo run, e.g. requirements.txt.
Same root cause class as a stray root-owned .git/FETCH_HEAD blocking a
plain `git pull` — a root-run leaving files a later unprivileged run can't
touch.

Fix: call ensure_docker_dir_ownership "$AS_DIR" unconditionally right
after the installer-run block, not only on the cloud-provider path.
This commit is contained in:
Claude
2026-08-21 04:05:27 +00:00
parent 9bf49b5daa
commit 1ff12d6643
+11
View File
@@ -124,6 +124,17 @@ install_ai-stack() {
log_info "Skipped. Run later: cd $AS_DIR && bash local-ai-setup.sh"
fi
# local-ai-setup.sh runs as whoever invoked this wrapper — root, since
# setup.sh itself is run via sudo — so everything it just generated
# (docker-compose.yml, .env, requirements.txt, server.py, pull-models.sh,
# etc.) comes out root-owned. Hand it back to ACTUAL_USER unconditionally,
# not just on the cloud-provider path below. Confirmed live: without this,
# re-running local-ai-setup.sh directly later (the update path, plain user,
# no sudo — exactly what its own "run later" message above tells you to do)
# fails with "Permission denied" on any file the first root-run created,
# e.g. requirements.txt.
ensure_docker_dir_ownership "$AS_DIR"
# ── Wire cloud providers into the generated compose ───────────────────────
if [ ${#CLOUD_NAMES[@]} -gt 0 ] && [ -f "$AS_DIR/docker-compose.yml" ]; then
# Prepend the local RAG connection so RAG keeps working, then the clouds.