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