Merge pull request #207 from yellowcooln/dev

This commit is contained in:
Lloyd
2026-04-26 22:56:30 +01:00
committed by GitHub
+176 -21
View File
@@ -61,6 +61,41 @@ fail() {
exit 1
}
run_with_spinner() {
local message="$1"
shift
if [ ! -t 1 ] || [ "${SILENT_MODE:-}" = "1" ] || [ "${SILENT_MODE:-}" = "true" ]; then
"$@"
return $?
fi
local log_file pid spinner_index status
local spinner='|/-\'
log_file=$(mktemp)
"$@" >"$log_file" 2>&1 &
pid=$!
spinner_index=0
while kill -0 "$pid" 2>/dev/null; do
printf '\r - %s... %s' "$message" "${spinner:$spinner_index:1}"
spinner_index=$(( (spinner_index + 1) % 4 ))
sleep 0.15
done
wait "$pid"
status=$?
printf '\r\033[K'
if [ "$status" -ne 0 ]; then
cat "$log_file" >&2
fi
rm -f "$log_file"
return "$status"
}
detect_local_git_ref() {
local branch tag
@@ -332,9 +367,10 @@ ensure_venv() {
stage "Creating virtual environment"
info "Creating $VENV_DIR"
info "This can take a minute on Buildroot flash storage."
python3 -m venv "$VENV_DIR"
info "Bootstrapping pip, setuptools, and wheel"
"$VENV_PIP" install --upgrade --no-cache-dir pip setuptools wheel
run_with_spinner "Creating virtual environment" python3 -m venv "$VENV_DIR"
info "Bootstrapping pip, setuptools, wheel, and setuptools_scm"
run_with_spinner "Bootstrapping Python build tools" \
"$VENV_PIP" install --upgrade --no-cache-dir pip setuptools wheel setuptools_scm
info "Virtual environment is ready"
else
info "Using existing virtual environment at $VENV_DIR"
@@ -345,6 +381,7 @@ ensure_venv_build_backend() {
if "$VENV_PYTHON" - <<'PY'
import setuptools
import setuptools.build_meta
import setuptools_scm
import wheel
PY
then
@@ -353,14 +390,16 @@ PY
fi
stage "Rebuilding virtual environment"
warn "Existing venv is contaminated or incomplete; recreating it cleanly."
warn "Existing venv is missing required Python build packages or has incompatible leftovers; recreating it cleanly. This can take a minute on Buildroot flash storage."
rm -rf "$VENV_DIR"
python3 -m venv "$VENV_DIR"
"$VENV_PIP" install --upgrade --no-cache-dir pip setuptools wheel
run_with_spinner "Recreating virtual environment" python3 -m venv "$VENV_DIR"
run_with_spinner "Reinstalling Python build tools" \
"$VENV_PIP" install --upgrade --no-cache-dir pip setuptools wheel setuptools_scm
if "$VENV_PYTHON" - <<'PY'
import setuptools
import setuptools.build_meta
import setuptools_scm
import wheel
PY
then
@@ -448,6 +487,34 @@ install_buildroot_dependencies() {
fi
}
check_buildroot_dependencies_installed() {
local deps
deps=$(set_wheel_dependencies)
"$VENV_PYTHON" - $deps <<'PY'
import sys
from importlib import metadata
from packaging.requirements import Requirement
from packaging.version import Version
failed = []
for raw in sys.argv[1:]:
req = Requirement(raw)
try:
installed = metadata.version(req.name)
except metadata.PackageNotFoundError:
failed.append(f"{req.name} (missing)")
continue
if req.specifier and Version(installed) not in req.specifier:
failed.append(f"{req.name} {installed} (! {req.specifier})")
if failed:
for item in failed:
print(item)
raise SystemExit(1)
PY
}
link_system_site_packages() {
local venv_site_dir pth_file system_paths
@@ -492,6 +559,37 @@ install_core_into_venv() {
"$VENV_PIP" install --upgrade --no-cache-dir --no-deps --no-build-isolation "$core_spec"
}
get_installed_core_commit() {
"$VENV_PYTHON" - <<'PY'
import glob
import json
import os
matches = glob.glob("/opt/pymc_repeater/venv/lib/python*/site-packages/pymc_core-*.dist-info/direct_url.json")
for path in matches:
try:
with open(path, "r", encoding="utf-8") as fh:
data = json.load(fh)
print(data.get("vcs_info", {}).get("commit_id", ""))
raise SystemExit(0)
except Exception:
pass
print("")
PY
}
resolve_core_commit() {
local core_repo core_ref
core_repo="$PYMC_CORE_REPO"
case "$core_repo" in
*.git) ;;
*) core_repo="${core_repo}.git" ;;
esac
core_ref=$(resolve_core_ref)
git ls-remote "$core_repo" "$core_ref" 2>/dev/null | awk 'NR==1 {print $1}'
}
install_repeater_package() {
stage "Installing pyMC Repeater into venv"
info "Installing checked-out repo without re-resolving dependencies"
@@ -997,6 +1095,14 @@ is_running() {
[ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null
}
start_or_restart_service() {
if is_running; then
"$INIT_SCRIPT" restart
else
"$INIT_SCRIPT" start
fi
}
get_version() {
if [ -x "$VENV_PYTHON" ]; then
"$VENV_PYTHON" -c "from importlib.metadata import version; print(version('pymc_repeater'))" 2>/dev/null || echo "not installed"
@@ -1005,6 +1111,27 @@ get_version() {
fi
}
prepare_git_version() {
local git_version
if [ -d "$SCRIPT_DIR/.git" ]; then
stage "Inspecting checked-out repo version"
info "Fetching tags for setuptools_scm version detection"
git -C "$SCRIPT_DIR" fetch --tags 2>/dev/null || true
git_version=$(python3 -m setuptools_scm 2>/dev/null || echo "1.0.5")
export SETUPTOOLS_SCM_PRETEND_VERSION="$git_version"
export SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYMC_REPEATER="$git_version"
info "Using version: $git_version"
else
git_version="1.0.5"
export SETUPTOOLS_SCM_PRETEND_VERSION="$git_version"
export SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYMC_REPEATER="$git_version"
info "Using fallback version: $git_version"
fi
printf '%s\n' "$git_version"
}
doctor() {
stage "Checking Buildroot image baseline"
@@ -1115,17 +1242,7 @@ install_repeater() {
stage "Cleaning legacy install state"
cleanup_legacy_install_state
if [ -d "$SCRIPT_DIR/.git" ]; then
stage "Inspecting checked-out repo version"
info "Fetching tags for setuptools_scm version detection"
git -C "$SCRIPT_DIR" fetch --tags 2>/dev/null || true
git_version=$(python3 -m setuptools_scm 2>/dev/null || echo "1.0.5")
export SETUPTOOLS_SCM_PRETEND_VERSION="$git_version"
info "Using version: $git_version"
else
export SETUPTOOLS_SCM_PRETEND_VERSION="1.0.5"
info "Using fallback version: 1.0.5"
fi
git_version=$(prepare_git_version)
preinstall_r2_wheels
install_buildroot_dependencies
@@ -1146,7 +1263,7 @@ install_repeater() {
create_init_script
stage "Starting service"
"$INIT_SCRIPT" restart
start_or_restart_service
ip_address=$(get_primary_ip)
if is_running; then
@@ -1157,19 +1274,44 @@ install_repeater() {
}
upgrade_repeater() {
local current_version new_version ip_address git_version
local target_core_commit installed_core_commit
ensure_root
is_installed || fail "Service is not installed."
current_version=$(get_version)
ensure_venv
ensure_venv_build_backend
stage "Cleaning legacy install state"
cleanup_legacy_install_state
git_version=$(prepare_git_version)
preinstall_r2_wheels
stage "Upgrading pyMC Repeater"
install_buildroot_dependencies
install_core_into_venv
install_repeater_package
if [ "${PYMC_FORCE_DEPS:-0}" = "1" ]; then
info "Forcing dependency reinstall"
install_buildroot_dependencies
elif check_buildroot_dependencies_installed >/dev/null 2>&1; then
info "Python dependency wheels already satisfy Buildroot requirements; skipping reinstall"
else
info "One or more Python dependency wheels are missing or out of range; reinstalling"
install_buildroot_dependencies
fi
target_core_commit=$(resolve_core_commit)
installed_core_commit=$(get_installed_core_commit)
if [ -n "$target_core_commit" ] && [ "$installed_core_commit" = "$target_core_commit" ] && [ "${PYMC_FORCE_CORE:-0}" != "1" ]; then
info "pyMC_core is already at ${target_core_commit}; skipping reinstall"
else
install_core_into_venv
fi
if [ "$current_version" = "$git_version" ] && [ "${PYMC_FORCE_REPEATER:-0}" != "1" ]; then
info "pyMC Repeater is already at ${git_version}; skipping reinstall"
else
install_repeater_package
fi
link_system_site_packages
stage "Validating installed runtime"
if check_venv_runtime; then
@@ -1177,7 +1319,20 @@ upgrade_repeater() {
else
fail "Installed packages are present but one or more native modules are unusable on this image."
fi
stage "Restarting service"
"$INIT_SCRIPT" restart
new_version=$(get_version)
ip_address=$(get_primary_ip)
if is_running; then
printf '\nUpgrade complete.\n'
printf 'Version: %s -> %s\n' "$current_version" "$new_version"
printf 'Service is running on: http://%s:8000\n' "${ip_address}"
else
fail "Upgrade completed but the service failed to start. Check: sh $0 logs"
fi
}
uninstall_repeater() {