From 7f29b1831e1b9d0f4527851aee6915c2ff494a61 Mon Sep 17 00:00:00 2001 From: grabowski Date: Sun, 18 Oct 2026 17:55:33 +0700 Subject: [PATCH] fix(install): find uv for the .venv owner (root shells and sudo lack ~/.local/bin) Run as root, SUDO_USER is unset and uv is not on root's PATH, so the training environment was not synced. Now the sync runs as the owner of .venv, with uv from PATH, that user's ~/.local/bin or ~/.cargo/bin, /usr/local/bin, or UV=/path/to/uv. --- scripts/install.sh | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/scripts/install.sh b/scripts/install.sh index 3a503a3..b10087f 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -20,9 +20,11 @@ # built beforehand as a normal user: # cargo build --release --locked --manifest-path rust/Cargo.toml # (Rust >= 1.88 from rustup, plus pkg-config and libssl-dev) -# 4. the Python training environment (.venv, `uv sync --frozen`): in place -# as the user running sudo (who pulled and built), otherwise as root; -# SKIP_UV=1 skips it +# 4. the Python training environment (.venv, `uv sync --frozen`), as the +# owner of .venv (in place; else the user running sudo) or as root for a +# copy. uv is looked up on PATH and in that user's ~/.local/bin and +# ~/.cargo/bin (sudo and root shells often lack them); UV=/path/to/uv +# overrides, SKIP_UV=1 skips the step # 5. .env: created from .env.example on a fresh install (edit, then re-run); # WEB_LISTEN and FORECAST_IN_COLLECTOR default to the production values # when absent @@ -104,16 +106,32 @@ log "Installing $BIN -> ${APP_DIR}/bin/ping-collector ($("$BIN" --version))" install -D -m 0755 -o root -g root "$BIN" "${APP_DIR}/bin/ping-collector" # 4. Python training environment ------------------------------------------------ +find_uv() { # user -> path of a uv binary that user can run + local home c + home="$(getent passwd "$1" | cut -d: -f6)" + for c in "${UV:-}" "$(command -v uv 2>/dev/null || true)" "${home}/.local/bin/uv" \ + "${home}/.cargo/bin/uv" /root/.local/bin/uv /usr/local/bin/uv /usr/bin/uv; do + [ -n "$c" ] && [ -x "$c" ] && runuser -u "$1" -- test -x "$c" && { + echo "$c" + return 0 + } + done + return 1 +} + if [ "${SKIP_UV:-0}" != 1 ]; then uv_user=root # copy mode: handed to the service user below - [ "$in_place" = 1 ] && uv_user="${SUDO_USER:-root}" - log "Syncing the Python training environment (${APP_DIR}/.venv) as $uv_user" - if [ "$uv_user" = root ]; then - (cd "$APP_DIR" && uv sync --python 3.11 --frozen) || - warn "uv sync failed: the monthly retrain needs ${APP_DIR}/.venv (uv sync --python 3.11 --frozen)" + if [ "$in_place" = 1 ]; then + uv_user="${SUDO_USER:-root}" + [ -d "${APP_DIR}/.venv" ] && uv_user="$(stat -c %U "${APP_DIR}/.venv")" + fi + if uv_bin="$(find_uv "$uv_user")"; then + log "Syncing the Python training environment (${APP_DIR}/.venv) as $uv_user with $uv_bin" + (cd "$APP_DIR" && runuser -u "$uv_user" -- env HOME="$(getent passwd "$uv_user" | cut -d: -f6)" \ + "$uv_bin" sync --python 3.11 --frozen) || + warn "uv sync failed: the monthly retrain needs ${APP_DIR}/.venv (uv sync --python 3.11 --frozen as $uv_user)" else - runuser -u "$uv_user" -- bash -lc "cd '$APP_DIR' && uv sync --python 3.11 --frozen" || - warn "uv sync as $uv_user failed: run 'uv sync --python 3.11 --frozen' in $APP_DIR as the owner of .venv" + warn "uv not found for $uv_user (pass UV=/path/to/uv): ${APP_DIR}/.venv not synced" fi fi