#!/bin/sh # OWILIX Installer (POSIX sh) # Usage: # curl -fsSL https://opencode.it4i.eu/openwebsearcheu-public/owi-cli/-/raw/main/scripts/install.sh?ref_type=heads | sh # # Options (via environment variables): # OWILIX_VERSION - Specific version to install (default: latest) # OWILIX_PATH - Installation directory for uv/pipx (default: ~/.venv/owilix) # OWILIX_NO_PATH - Set to 1 to skip PATH modification # OWILIX_UPGRADE - Set to 1 for upgrade mode # OWILIX_PM - Force package manager: uv, conda, mamba, pipx (skip selection) set -eu # ---- Colors (safe-ish for sh) ---- RED=$(printf '\033[0;31m') GREEN=$(printf '\033[0;32m') YELLOW=$(printf '\033[1;33m') BLUE=$(printf '\033[0;34m') CYAN=$(printf '\033[0;36m') NC=$(printf '\033[0m') log() { printf "%s[owilix]%s %s\n" "$BLUE" "$NC" "$1"; } success() { printf "%s[owilix]%s %s\n" "$GREEN" "$NC" "$1"; } warn() { printf "%s[owilix]%s %s\n" "$YELLOW" "$NC" "$1"; } error() { printf "%s[owilix]%s %s\n" "$RED" "$NC" "$1" >&2; } # ---- Config ---- OWILIX_VERSION=${OWILIX_VERSION:-} OWILIX_PATH=${OWILIX_PATH:-"$HOME/.venv/owilix"} OWILIX_NO_PATH=${OWILIX_NO_PATH:-0} OWILIX_UPGRADE=${OWILIX_UPGRADE:-0} OWILIX_PM=${OWILIX_PM:-} OWILIX_INDEX="https://opencode.it4i.eu/api/v4/projects/92/packages/pypi/simple" PY4LEXIS_INDEX="https://opencode.it4i.eu/api/v4/projects/107/packages/pypi/simple" # uv requires URL dependencies to be expressed as direct requirements. # Keep this in sync with pyproject.toml to avoid URL conflicts. GIT_DEP="python-http-irods-client @ git+https://opencode.it4i.eu/lexis-platform/data/python-http-irods-client.git@1.1.4" # Keep py4lexis deterministic for installer resolution, since newer py4lexis # releases may change URL-pinned transitive dependencies. PY4LEXIS_DEP="py4lexis==5.4.0" # Fix for OSTYPE not set in some environments (causes issues with set -u) OSTYPE=${OSTYPE:-unknown} export OSTYPE # ---- Helpers ---- have() { command -v "$1" >/dev/null 2>&1; } detect_os() { case "$(uname -s 2>/dev/null || echo unknown)" in Linux*) OS="linux" ;; Darwin*) OS="macos" ;; MINGW*|CYGWIN*|MSYS*) OS="windows" ;; *) OS="unknown" ;; esac log "Detected OS: $OS" } detect_package_managers() { AVAILABLE_PMS="" have uv && AVAILABLE_PMS="$AVAILABLE_PMS uv" have conda && AVAILABLE_PMS="$AVAILABLE_PMS conda" have mamba && AVAILABLE_PMS="$AVAILABLE_PMS mamba" have pipx && AVAILABLE_PMS="$AVAILABLE_PMS pipx" if [ -n "$AVAILABLE_PMS" ]; then log "Available package managers:${AVAILABLE_PMS}" else log "Available package managers: none" fi } count_words() { # prints number of words in $1 # shellcheck disable=SC2086 set -- $1 echo $# } nth_word() { # nth_word "a b c" 2 -> b list=$1 n=$2 i=1 for w in $list; do if [ "$i" -eq "$n" ]; then echo "$w" return 0 fi i=$((i + 1)) done return 1 } install_uv() { if have uv; then log "uv is already installed: $(uv --version 2>/dev/null || echo unknown)" return 0 fi log "Installing uv..." if have curl; then curl -fsSL https://astral.sh/uv/install.sh | sh elif have wget; then wget -qO- https://astral.sh/uv/install.sh | sh else error "Neither curl nor wget found. Please install one first." exit 1 fi # Common install location for uv PATH="$HOME/.local/bin:$PATH" export PATH if have uv; then success "uv installed: $(uv --version 2>/dev/null || echo unknown)" AVAILABLE_PMS="$AVAILABLE_PMS uv" else error "Failed to install uv" exit 1 fi } choose_package_manager() { if [ -n "$OWILIX_PM" ]; then PM=$OWILIX_PM log "Using package manager (from OWILIX_PM): $PM" return 0 fi n=$(count_words "$AVAILABLE_PMS") if [ "$n" -eq 1 ]; then PM=$(nth_word "$AVAILABLE_PMS" 1) log "Using package manager: $PM" return 0 fi if [ "$n" -eq 0 ]; then log "No package managers found. Installing uv..." install_uv PM="uv" return 0 fi echo "" printf "%sMultiple package managers detected. Please choose:%s\n\n" "$CYAN" "$NC" i=1 for pm in $AVAILABLE_PMS; do case "$pm" in uv) printf " %s) uv - Fast, recommended (creates venv at %s)\n" "$i" "$OWILIX_PATH" ;; conda) printf " %s) conda - Creates 'owilix' conda environment\n" "$i" ;; mamba) printf " %s) mamba - Fast conda alternative (creates 'owilix' env)\n" "$i" ;; pipx) printf " %s) pipx - Isolated install (creates venv at %s)\n" "$i" "$OWILIX_PATH" ;; esac i=$((i + 1)) done echo "" # Read from tty so curl|sh still prompts printf "Enter choice [1-%s] (default: 1): " "$n" > /dev/tty read -r choice < /dev/tty || true [ -z "${choice:-}" ] && choice=1 case "$choice" in *[!0-9]*|'') choice=1 ;; esac if [ "$choice" -lt 1 ] || [ "$choice" -gt "$n" ]; then choice=1 fi PM=$(nth_word "$AVAILABLE_PMS" "$choice" || true) [ -z "${PM:-}" ] && PM=$(nth_word "$AVAILABLE_PMS" 1) log "Selected package manager: $PM" } handle_existing_path() { if [ -d "$OWILIX_PATH" ]; then warn "Installation directory exists: $OWILIX_PATH" printf "Remove and reinstall? [y/N] " > /dev/tty read -r reply < /dev/tty || true case "$reply" in y|Y) rm -rf "$OWILIX_PATH" ;; *) error "Installation aborted"; exit 1 ;; esac fi } # ---- Installers ---- install_with_uv() { version_arg="" [ -n "$OWILIX_VERSION" ] && version_arg="==$OWILIX_VERSION" if [ "$OWILIX_UPGRADE" = "1" ] && [ -d "$OWILIX_PATH" ]; then log "Upgrading with uv..." # shellcheck disable=SC1090 [ -f "$OWILIX_PATH/bin/activate" ] && . "$OWILIX_PATH/bin/activate" || true uv pip install --upgrade "$GIT_DEP" "$PY4LEXIS_DEP" "owilix${version_arg}" \ --index-url "$OWILIX_INDEX" \ --extra-index-url "$PY4LEXIS_INDEX" return 0 fi handle_existing_path log "Creating virtual environment at $OWILIX_PATH..." uv venv --python 3.11 "$OWILIX_PATH" log "Installing owilix with uv..." # shellcheck disable=SC1090 . "$OWILIX_PATH/bin/activate" uv pip install "$GIT_DEP" "$PY4LEXIS_DEP" "owilix${version_arg}" \ --index-url "$OWILIX_INDEX" \ --extra-index-url "$PY4LEXIS_INDEX" } conda_hook() { # Make conda activate work in non-interactive sh if have conda; then # Prefer posix hook, fall back to sh hook eval "$(conda shell.posix hook 2>/dev/null || conda shell.sh hook 2>/dev/null)" || true fi } install_with_conda() { version_arg="" [ -n "$OWILIX_VERSION" ] && version_arg="==$OWILIX_VERSION" env_name="owilix" conda_hook if [ "$OWILIX_UPGRADE" = "1" ]; then log "Upgrading with conda..." conda activate "$env_name" >/dev/null 2>&1 || true pip install --upgrade "owilix${version_arg}" \ --index-url "$OWILIX_INDEX" \ --extra-index-url "$PY4LEXIS_INDEX" else if conda env list 2>/dev/null | grep -q "^$env_name[[:space:]]"; then warn "Conda environment '$env_name' exists" printf "Remove and recreate? [y/N] " > /dev/tty read -r reply < /dev/tty || true case "$reply" in y|Y) conda env remove -n "$env_name" -y ;; *) error "Installation aborted"; exit 1 ;; esac fi log "Creating conda environment '$env_name'..." conda create -n "$env_name" python=3.11 -y log "Installing owilix with conda/pip..." conda activate "$env_name" pip install "owilix${version_arg}" \ --index-url "$OWILIX_INDEX" \ --extra-index-url "$PY4LEXIS_INDEX" fi success "owilix installed in conda env '$env_name'" warn "To use: conda activate $env_name" } install_with_mamba() { version_arg="" [ -n "$OWILIX_VERSION" ] && version_arg="==$OWILIX_VERSION" env_name="owilix" conda_hook if [ "$OWILIX_UPGRADE" = "1" ]; then log "Upgrading with mamba..." # activation varies; try conda hook/activate conda activate "$env_name" >/dev/null 2>&1 || true pip install --upgrade "owilix${version_arg}" \ --index-url "$OWILIX_INDEX" \ --extra-index-url "$PY4LEXIS_INDEX" else if (have mamba && mamba env list 2>/dev/null | grep -q "^$env_name[[:space:]]") \ || (have conda && conda env list 2>/dev/null | grep -q "^$env_name[[:space:]]"); then warn "Mamba/conda environment '$env_name' exists" printf "Remove and recreate? [y/N] " > /dev/tty read -r reply < /dev/tty || true case "$reply" in y|Y) (have mamba && mamba env remove -n "$env_name" -y >/dev/null 2>&1) || true (have conda && conda env remove -n "$env_name" -y >/dev/null 2>&1) || true ;; *) error "Installation aborted"; exit 1 ;; esac fi log "Creating mamba environment '$env_name'..." mamba create -n "$env_name" python=3.11 -y log "Installing owilix with mamba/pip..." conda activate "$env_name" pip install "owilix${version_arg}" \ --index-url "$OWILIX_INDEX" \ --extra-index-url "$PY4LEXIS_INDEX" fi success "owilix installed in mamba env '$env_name'" warn "To use: mamba activate $env_name (or conda activate $env_name)" } install_with_pipx() { version_arg="" [ -n "$OWILIX_VERSION" ] && version_arg="==$OWILIX_VERSION" if [ "$OWILIX_UPGRADE" = "1" ]; then log "Upgrading with pipx..." pipx upgrade owilix >/dev/null 2>&1 || true else log "Installing owilix with pipx..." # pipx doesn't handle git deps well, try pipx and fall back to uv if pipx install "owilix${version_arg}" \ --index-url "$OWILIX_INDEX" \ --pip-args="--extra-index-url=$PY4LEXIS_INDEX" >/dev/null 2>&1 then : else warn "pipx install failed (dependency resolution issue)" log "Falling back to uv installation..." install_uv PM="uv" install_with_uv return 0 fi fi success "owilix installed with pipx" } # ---- PATH setup ---- setup_path() { # Skip for conda/mamba (they use activation) if [ "$PM" = "conda" ] || [ "$PM" = "mamba" ]; then return 0 fi if [ "$OWILIX_NO_PATH" = "1" ]; then log "Skipping PATH modification (OWILIX_NO_PATH=1)" return 0 fi path_line="export PATH=\"$OWILIX_PATH/bin:\$PATH\"" shell_rc="" # In sh we can't reliably know the user's interactive shell; best effort: case "${SHELL:-}" in *zsh*) shell_rc="$HOME/.zshrc" ;; *bash*) if [ "$OS" = "macos" ]; then shell_rc="$HOME/.bash_profile" else shell_rc="$HOME/.bashrc" fi ;; *) # POSIX default shell_rc="$HOME/.profile" ;; esac if [ -n "$shell_rc" ]; then if [ -f "$shell_rc" ] && grep -qi "owilix" "$shell_rc" 2>/dev/null; then log "PATH already configured in $shell_rc" else { echo "" echo "# OWILIX CLI" echo "$path_line" } >> "$shell_rc" success "Added owilix to PATH in $shell_rc" fi fi } # ---- Config migration ---- check_migration() { owilix_bin="" case "$PM" in uv|pipx) owilix_bin="$OWILIX_PATH/bin/owilix" ;; conda|mamba) owilix_bin="owilix" ;; *) owilix_bin="owilix" ;; esac log "Checking configuration status..." if ! "$owilix_bin" config version >/dev/null 2>&1; then log "No existing configuration found" return 0 fi version_output=$("$owilix_bin" config version 2>&1 || true) echo "$version_output" echo "$version_output" | grep -qi "migration available\|upgrade\|outdated" || return 0 echo "" printf "Config migration available. Run migration now? [Y/n] " > /dev/tty read -r reply < /dev/tty || true case "$reply" in n|N) return 0 ;; esac log "Running migration (dry-run first)..." "$owilix_bin" config migrate --dry-run >/dev/null 2>&1 || true echo "" printf "Apply migration? [y/N] " > /dev/tty read -r reply2 < /dev/tty || true case "$reply2" in y|Y) "$owilix_bin" config migrate success "Migration completed" ;; esac } print_usage() { echo "" success "╔════════════════════════════════════════════════════════════════╗" success "║ OWILIX installed successfully! ║" success "╚════════════════════════════════════════════════════════════════╝" echo "" case "$PM" in uv|pipx) echo " To use (after restarting terminal):" echo " owilix --help" ;; conda) echo " To use:" echo " conda activate owilix" echo " owilix --help" ;; mamba) echo " To use:" echo " mamba activate owilix # or: conda activate owilix" echo " owilix --help" ;; esac echo "" echo " Quick start:" echo " owilix remote doctor # Check connection" echo " owilix remote ls all # List datasets" echo "" echo " Documentation:" echo " https://openwebsearcheu-public.pages.it4i.eu/owi-cli/" echo "" } main() { echo "" log "╔════════════════════════════════════════════════════════════════╗" log "║ OWILIX Installer (sh) ║" log "╚════════════════════════════════════════════════════════════════╝" echo "" detect_os detect_package_managers choose_package_manager case "$PM" in uv) install_with_uv ;; conda) install_with_conda ;; mamba) install_with_mamba ;; pipx) install_with_pipx ;; *) error "Unknown package manager: $PM" exit 1 ;; esac setup_path check_migration print_usage } main "$@"