diff --git a/scripts/setup/officer-setup.sh b/scripts/setup/officer-setup.sh index 59ac1287..3a154076 100755 --- a/scripts/setup/officer-setup.sh +++ b/scripts/setup/officer-setup.sh @@ -42,6 +42,8 @@ source "$SCRIPT_DIR/officer-setup/lib/base.sh" source "$SCRIPT_DIR/officer-setup/lib/preflight.sh" # shellcheck source=officer-setup/lib/repo.sh source "$SCRIPT_DIR/officer-setup/lib/repo.sh" +# shellcheck source=officer-setup/lib/layout.sh +source "$SCRIPT_DIR/officer-setup/lib/layout.sh" trap 'echo ""; echo -e "${RED}╔══════════════════════════════════════════════════╗${NC}"; echo -e "${RED}║ OFFICER SETUP FAILED${NC}"; echo -e "${RED}║ Step: ${CURRENT_STEP:-unknown}${NC}"; echo -e "${RED}║ Line: $LINENO${NC}"; echo -e "${RED}║ Command: $BASH_COMMAND${NC}"; echo -e "${RED}╚══════════════════════════════════════════════════╝${NC}"' ERR @@ -156,7 +158,52 @@ else fi # ============================================================================= -# 2. Repository +# 2. Layout +# ============================================================================= +# +# Before the repository, because the repository is cloned into it. + +step "Layout" +if ! skip; then + echo "" + info "Layout — everything Officer owns, under one root" + echo " ${OFFICER_ROOT}/" + echo " platform/ the app" + echo " data/ managed homes, attachments, job logs" + echo " dockers/ anything the app store provisions" + echo " capabilities/ skills, tools, tasks, processes" + echo "" + echo " Nothing here is configurable. The original asked separately for the" + echo " data directory and the item store, which were two answers that had" + echo " to agree with each other. One root now, and the rest follows." + echo "" + echo " To put data/ on a bigger volume later, symlink it — that is a" + echo " decision about storage rather than about how Officer is laid out." + + mapfile -t WRONG_OWNER < <(layout_wrong_owner) + if ((${#WRONG_OWNER[@]} > 0)); then + echo "" + warn "these exist but do not belong to ${USERNAME}:" + printf ' %s\n' "${WRONG_OWNER[@]}" + echo " Everything that writes into them runs as ${USERNAME} — the platform" + echo " under pm2, the app store's compose files, the item store the agent" + echo " authors into. Left as they are, those writes fail in a way that" + echo " reads as a bug in the platform." + if confirm "Give them to ${USERNAME}?"; then + for d in "${WRONG_OWNER[@]}"; do chown -R "${USERNAME}:$(user_group)" "$d"; done + ok "ownership corrected" + SUMMARY+=("Layout: ownership corrected on ${#WRONG_OWNER[@]} directory(ies)") + fi + fi + + create_layout + ok "layout in place under ${OFFICER_ROOT}" + SUMMARY+=("Layout: ${OFFICER_ROOT} (data, dockers, capabilities)") + step_ok +fi + +# ============================================================================= +# 3. Repository # ============================================================================= step "Repository" @@ -228,7 +275,7 @@ if ! skip; then fi # ============================================================================= -# 3. Dependencies +# 4. Dependencies # ============================================================================= step "Dependencies" diff --git a/scripts/setup/officer-setup/lib/layout.sh b/scripts/setup/officer-setup/lib/layout.sh new file mode 100644 index 00000000..9560b797 --- /dev/null +++ b/scripts/setup/officer-setup/lib/layout.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# ============================================================================= +# officer-setup — the install layout +# ============================================================================= +# +# Definitions only. +# +# ── One root, and nothing configurable underneath it ── +# +# $OFFICER_ROOT/ +# platform/ the app — the git checkout +# data/ DATA_PATH: managed homes, attachments, job logs +# dockers/ services the app store provisioned +# capabilities/ the file-based item store — skills, tools, tasks, processes +# +# The original asked separately for DATA_PATH and for OFFICER_ITEMS_DIR, and left +# the app store's directory implicit. Three answers that had to agree with each +# other, given by somebody with no reason to know they had to. +# +# Now one question — where the root goes — and the rest follows. Anybody who wants +# data/ on a bigger volume can symlink it; that is a decision about storage, not +# about how Officer is laid out, and it does not need a prompt in a setup script. +# +# This is also what the code already assumes. app-store/paths.ts derives +# OFFICER_ROOT as dirname(DATA_PATH) and DOCKERS_DIR as OFFICER_ROOT/dockers, so +# setting DATA_PATH to /data is the whole of what makes the layout correct. + +[[ -n "${OFFICER_SETUP_LAYOUT_LOADED:-}" ]] && return 0 +OFFICER_SETUP_LAYOUT_LOADED=1 + +layout_data_dir() { echo "${OFFICER_ROOT}/data"; } +layout_dockers_dir() { echo "${OFFICER_ROOT}/dockers"; } +layout_items_dir() { echo "${OFFICER_ROOT}/capabilities"; } + +layout_dirs() { + echo "$OFFICER_ROOT" + echo "$(layout_data_dir)" + echo "$(layout_dockers_dir)" + echo "$(layout_items_dir)" +} + +# Created owned by the account, because everything that writes into them runs as +# the account: the platform under pm2, the app store's compose files, the item +# store the agent authors into. +create_layout() { + local dir + while read -r dir; do + [[ -d "$dir" ]] || install -d -m 0755 -o "$USERNAME" -g "$(user_group)" "$dir" + done < <(layout_dirs) + return 0 +} + +# A directory that exists but belongs to somebody else is the failure this +# reports: it happens when an earlier run, or a hand-made directory, was created +# as root, and everything written into it afterwards fails in a way that reads as +# a permissions bug in the platform. +layout_wrong_owner() { + local dir + while read -r dir; do + [[ -d "$dir" ]] || continue + [[ "$(stat -c %U "$dir")" == "$USERNAME" ]] || echo "$dir" + done < <(layout_dirs) +}