From 18bca70c8f6de765b1304d20209a3401e2f241a1 Mon Sep 17 00:00:00 2001 From: clsferguson <48876201+clsferguson@users.noreply.github.com> Date: Thu, 11 Sep 2025 10:13:25 -0600 Subject: [PATCH] Improve logging and ownership management in entrypoint.sh --- entrypoint.sh | 86 ++++++++++++++++++++------------------------------- 1 file changed, 33 insertions(+), 53 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 3cdc74dbe..7004b270c 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,101 +1,82 @@ #!/bin/bash -set -Eeuo pipefail - -# Timestamped xtrace (enable by uncommenting: set -x) -export PS4='+ [${EPOCHREALTIME}] ${BASH_SOURCE##*/}:${LINENO}: ' -# set -x - -log() { printf '[entrypoint] %s\n' "$*" >&2; } +set -euo pipefail APP_USER=${APP_USER:-appuser} APP_GROUP=${APP_GROUP:-appuser} PUID=${PUID:-1000} PGID=${PGID:-1000} + BASE_DIR=/app/ComfyUI CUSTOM_NODES_DIR="$BASE_DIR/custom_nodes" +# If running as root, map to requested UID/GID, fix ownership, and make Python install targets writable if [ "$(id -u)" = "0" ]; then - log "Running as root; mapping to UID=${PUID} GID=${PGID} and fixing ownerships" # Map group to PGID if it already exists, otherwise remap the named group if getent group "${PGID}" >/dev/null; then EXISTING_GRP="$(getent group "${PGID}" | cut -d: -f1)" - log "Using existing group ${EXISTING_GRP} for GID ${PGID}" usermod -g "${EXISTING_GRP}" "${APP_USER}" || true APP_GROUP="${EXISTING_GRP}" else - log "Remapping group ${APP_GROUP} to GID ${PGID}" groupmod -o -g "${PGID}" "${APP_GROUP}" || true fi # Map user to PUID - log "Remapping user ${APP_USER} to UID ${PUID}" usermod -o -u "${PUID}" "${APP_USER}" || true # Ensure home and app dir exist and are owned mkdir -p "/home/${APP_USER}" for d in "$BASE_DIR" "/home/$APP_USER"; do - if [ -e "$d" ]; then - log "Ensuring ownership ${APP_USER}:${APP_GROUP} on ${d}" - chown -R "${APP_USER}:${APP_GROUP}" "$d" || true - fi + [ -e "$d" ] && chown -R "${APP_USER}:${APP_GROUP}" "$d" || true done - # Discover Python system install targets and make them writable for runtime user + # Make Python system install targets writable for the runtime user # Includes: site-packages (purelib/platlib), scripts, headers, data, and data/share(+man1) # Discards anything not under /usr/local to avoid over-broad changes. - log "Discovering Python install paths via sysconfig (unbuffered Python logging enabled)" - readarray -t PY_PATHS < <( - env PYTHONUNBUFFERED=1 python - <<'PY' -import sys, os, sysconfig + readarray -t PY_PATHS < <(python - <<'PY' +import sys, sysconfig, os, datetime -def emit(label, path): - if not path: - return - # stdout: raw path for readarray - print(path) - # stderr: labeled diagnostics for logs - print(f"[py] {label}: {path}", file=sys.stderr, flush=True) +def log(msg): + ts = datetime.datetime.now().strftime("%H:%M:%S") + print(f"[bootstrap:python {ts}] {msg}", file=sys.stderr, flush=True) -p = sysconfig.get_paths() -for k in ("purelib","platlib","scripts","include","platinclude","data"): - emit(k, p.get(k)) +log("Determining writable Python install targets via sysconfig.get_paths()") +keys = ("purelib","platlib","scripts","include","platinclude","data") +paths = sysconfig.get_paths() +for k in keys: + v = paths.get(k) + if v: + print(v) # stdout for the shell to capture + log(f"emit {k} -> {v}") # stderr for the user to see -d = p.get("data") +d = paths.get("data") if d: - # Wheel .data/data payloads commonly land under {data}/share, including manpages like ttx.1 - emit("share", os.path.join(d, "share")) - emit("man1", os.path.join(d, "share", "man", "man1")) + share = os.path.join(d, "share") + man1 = os.path.join(share, "man", "man1") + print(share) # stdout + print(man1) # stdout + log(f"emit wheel data dirs -> {share}, {man1}") # stderr + +log("Finished emitting target directories") PY - ) - - log "Found ${#PY_PATHS[@]} Python target paths:" - for p in "${PY_PATHS[@]}"; do - printf ' - %s\n' "$p" >&2 - done - +) for d in "${PY_PATHS[@]}"; do case "$d" in /usr/local/*) - log "Ensuring writable under /usr/local: ${d}" mkdir -p "$d" || true chown -R "${APP_USER}:${APP_GROUP}" "$d" || true chmod -R u+rwX,g+rwX "$d" || true ;; - *) - log "Skipping non-/usr/local path: ${d}" - ;; + *) : ;; esac done - log "Re-exec as runtime user ${APP_USER} (UID=${PUID}, GID=${PGID})" + # Re-exec as the runtime user exec runuser -u "${APP_USER}" -- "$0" "$@" fi -log "Running as $(id -un) (UID=$(id -u))" - # Ensure ComfyUI-Manager exists (bind mounts can hide baked content) if [ ! -d "$CUSTOM_NODES_DIR/ComfyUI-Manager" ]; then - log "Installing ComfyUI-Manager into $CUSTOM_NODES_DIR/ComfyUI-Manager" + echo "[bootstrap] Installing ComfyUI-Manager into $CUSTOM_NODES_DIR/ComfyUI-Manager" git clone --depth 1 https://github.com/ltdrdata/ComfyUI-Manager.git "$CUSTOM_NODES_DIR/ComfyUI-Manager" || true fi @@ -106,19 +87,18 @@ export PYTHONPATH="$HOME/.local/lib/python${pyver}/site-packages:${PYTHONPATH:-} # Auto-install custom node deps if [ "${COMFY_AUTO_INSTALL:-1}" = "1" ]; then - log "[deps] Scanning custom nodes for requirements..." + echo "[deps] Scanning custom nodes for requirements..." while IFS= read -r -d '' req; do - log "[deps] pip install --user -r $req" + echo "[deps] pip install --user -r $req" pip install --no-cache-dir --user -r "$req" || true done < <(find "$CUSTOM_NODES_DIR" -maxdepth 3 -type f \( -iname 'requirements.txt' -o -iname 'requirements-*.txt' -o -path '*/requirements/*.txt' \) -print0) while IFS= read -r -d '' pjt; do d="$(dirname "$pjt")" - log "[deps] pip install --user . in $d" + echo "[deps] pip install --user . in $d" (cd "$d" && pip install --no-cache-dir --user .) || true done < <(find "$CUSTOM_NODES_DIR" -maxdepth 2 -type f -iname 'pyproject.toml' -print0) - log "[deps] pip check (sanity)" pip check || true fi