#!/usr/bin/env sh # Linux deployment. No .env sourcing; all commands run from this checkout. set -eu if [ "${1:-}" = '--env-file-only' ]; then export DEPLOY_API_ENV_FILE_ONLY=1 shift fi case "$0" in */*) SCRIPT_DIR=${0%/*} ;; *) SCRIPT_DIR=. ;; esac REPO_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR" && pwd) cd "$REPO_ROOT" ACTION=${1:-deploy} case "$ACTION" in help|-h|--help) echo 'Optional first argument: --env-file-only (QCC/DeepSeek and DMS connection settings from .env; direct process restart inherits; systemd needs unit configuration)' echo 'Usage: sh deploy.sh [deploy|update-knowledge|restart|start|stop|status|check "company full name"|check-milvus|check-redis|check-dms|check-deepseek|check-qcc "company full name"]' echo 'deploy: install uv/Python/dependencies/models; initialize missing knowledge; restart API' echo 'update-knowledge: prepare/verify with old API running; then stop, publish and start' echo 'restart: sync isolated dependencies; stop this checkout API and start new code' echo 'check: run all five diagnostics; check-milvus/redis/dms/deepseek/qcc: run only one' echo 'Configuration and operational details: docs/deployment.md' exit 0 ;; deploy|update-knowledge|restart|start|stop|status|serve|check|check-milvus|check-redis|check-dms|check-deepseek|check-qcc) ;; *) echo 'Unknown deployment action. Run sh deploy.sh help.' >&2; exit 2 ;; esac case "$ACTION" in check|check-qcc) [ "$#" -eq 2 ] && [ -n "$2" ] || { echo 'This check requires one exact company name.' >&2; exit 2; } ;; *) [ "$#" -le 1 ] || { echo 'Only one action is accepted.' >&2; exit 2; } ;; esac [ "$(uname -s)" = Linux ] || { echo 'deploy.sh requires Linux with /proc.' >&2; exit 2; } export UV_CACHE_DIR="${UV_CACHE_DIR:-$REPO_ROOT/.uv-cache}" case "$ACTION" in start|stop|status|serve|check|check-milvus|check-redis|check-dms|check-deepseek|check-qcc) SERVICE_PYTHON="$REPO_ROOT/.venv/bin/python" if [ -f output/deploy/python-path ]; then IFS= read -r SERVICE_PYTHON < output/deploy/python-path fi [ -x "$SERVICE_PYTHON" ] || { echo 'Run sh deploy.sh deploy first.' >&2; exit 1; } case "$ACTION" in check|check-qcc) exec "$SERVICE_PYTHON" scripts/deploy.py "$ACTION" "$2" ;; esac exec "$SERVICE_PYTHON" scripts/deploy.py "$ACTION" ;; esac export PATH="$HOME/.local/bin:$PATH" unset DEPLOY_OFFLINE UV_OFFLINE if ! command -v uv >/dev/null 2>&1; then [ "${AUTO_INSTALL_UV:-1}" = 1 ] || { echo 'uv missing; AUTO_INSTALL_UV=0.' >&2; exit 1; } installer=$(mktemp) trap 'rm -f "$installer"' EXIT HUP INT TERM if command -v curl >/dev/null 2>&1; then curl --fail --location --silent --show-error https://astral.sh/uv/install.sh -o "$installer" else wget -q https://astral.sh/uv/install.sh -O "$installer" fi UV_NO_MODIFY_PATH=1 sh "$installer" rm -f "$installer" trap - EXIT HUP INT TERM fi command -v uv >/dev/null 2>&1 || { echo 'uv installation failed.' >&2; exit 1; } PYTHON_VERSION=$(tr -d '\r\n' < .python-version) uv python install "$PYTHON_VERSION" DEPLOY_PYTHON=$(uv python find --managed-python "$PYTHON_VERSION") # The controller is outside service environments; each sync builds a new environment. exec "$DEPLOY_PYTHON" scripts/deploy.py "$ACTION"