From 707c816471fcba05b45190a08da9ee414b9f76d3 Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:06:00 -0400 Subject: [PATCH 1/7] Start service cleanly on first Buildroot install --- buildroot-manage.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/buildroot-manage.sh b/buildroot-manage.sh index 6a0aa68..af4f43a 100644 --- a/buildroot-manage.sh +++ b/buildroot-manage.sh @@ -997,6 +997,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" @@ -1146,7 +1154,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 From 971ad4d2c92cf667377b909eff13133665a89f6e Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Sat, 25 Apr 2026 12:29:27 -0400 Subject: [PATCH 2/7] Preserve setuptools_scm version on Buildroot installs --- buildroot-manage.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/buildroot-manage.sh b/buildroot-manage.sh index af4f43a..ca77642 100644 --- a/buildroot-manage.sh +++ b/buildroot-manage.sh @@ -1129,9 +1129,11 @@ install_repeater() { 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 export SETUPTOOLS_SCM_PRETEND_VERSION="1.0.5" + export SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYMC_REPEATER="1.0.5" info "Using fallback version: 1.0.5" fi From d214885c1e0fba8c6cf8911f78d0e1822683666d Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Sat, 25 Apr 2026 12:38:19 -0400 Subject: [PATCH 3/7] Install setuptools_scm in Buildroot venv --- buildroot-manage.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/buildroot-manage.sh b/buildroot-manage.sh index ca77642..cbb6b83 100644 --- a/buildroot-manage.sh +++ b/buildroot-manage.sh @@ -333,8 +333,8 @@ ensure_venv() { 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 + info "Bootstrapping pip, setuptools, wheel, and setuptools_scm" + "$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 +345,7 @@ ensure_venv_build_backend() { if "$VENV_PYTHON" - <<'PY' import setuptools import setuptools.build_meta +import setuptools_scm import wheel PY then @@ -356,11 +357,12 @@ PY warn "Existing venv is contaminated or incomplete; recreating it cleanly." rm -rf "$VENV_DIR" python3 -m venv "$VENV_DIR" - "$VENV_PIP" install --upgrade --no-cache-dir pip setuptools wheel + "$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 From 8418fd5b62b77e44657d247cd0079ee9281f7f58 Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Sat, 25 Apr 2026 12:43:05 -0400 Subject: [PATCH 4/7] Clarify Buildroot venv rebuild message --- buildroot-manage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildroot-manage.sh b/buildroot-manage.sh index cbb6b83..7cf1237 100644 --- a/buildroot-manage.sh +++ b/buildroot-manage.sh @@ -354,7 +354,7 @@ 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 setuptools_scm From 5c7bcabee091b6e13955768ed3b421f0a0894c0a Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Sat, 25 Apr 2026 12:44:15 -0400 Subject: [PATCH 5/7] Add spinner for Buildroot venv rebuilds --- buildroot-manage.sh | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/buildroot-manage.sh b/buildroot-manage.sh index 7cf1237..12e4e7c 100644 --- a/buildroot-manage.sh +++ b/buildroot-manage.sh @@ -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" + run_with_spinner "Creating virtual environment" python3 -m venv "$VENV_DIR" info "Bootstrapping pip, setuptools, wheel, and setuptools_scm" - "$VENV_PIP" install --upgrade --no-cache-dir pip setuptools wheel 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" @@ -356,8 +392,9 @@ PY stage "Rebuilding virtual environment" 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 setuptools_scm + 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 From 42068e2b1ea74266a7a46de83fc2c84dd0772830 Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Sat, 25 Apr 2026 12:49:10 -0400 Subject: [PATCH 6/7] Print clear Buildroot upgrade completion --- buildroot-manage.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/buildroot-manage.sh b/buildroot-manage.sh index 12e4e7c..fe67f5c 100644 --- a/buildroot-manage.sh +++ b/buildroot-manage.sh @@ -1206,8 +1206,11 @@ install_repeater() { } upgrade_repeater() { + local current_version new_version ip_address + ensure_root is_installed || fail "Service is not installed." + current_version=$(get_version) ensure_venv ensure_venv_build_backend @@ -1226,7 +1229,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() { From 066fcef9c995bf520a372988873fdfe3a48a5589 Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Sat, 25 Apr 2026 12:53:47 -0400 Subject: [PATCH 7/7] Skip unchanged Buildroot upgrade steps --- buildroot-manage.sh | 124 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 107 insertions(+), 17 deletions(-) diff --git a/buildroot-manage.sh b/buildroot-manage.sh index fe67f5c..39c9a6b 100644 --- a/buildroot-manage.sh +++ b/buildroot-manage.sh @@ -487,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 @@ -531,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" @@ -1052,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" @@ -1162,19 +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" - export SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYMC_REPEATER="$git_version" - info "Using version: $git_version" - else - export SETUPTOOLS_SCM_PRETEND_VERSION="1.0.5" - export SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYMC_REPEATER="1.0.5" - info "Using fallback version: 1.0.5" - fi + git_version=$(prepare_git_version) preinstall_r2_wheels install_buildroot_dependencies @@ -1206,7 +1274,8 @@ install_repeater() { } upgrade_repeater() { - local current_version new_version ip_address + 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." @@ -1216,12 +1285,33 @@ upgrade_repeater() { 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