feat: improve ota packaging and people-flow runtime
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
RELEASE_VERSION="${RELEASE_VERSION:-20260513-330373b-11}"
|
||||
RELEASE_VERSION="${RELEASE_VERSION:-20260518-7b32b21-11}"
|
||||
BASE_URL="${BASE_URL:-http://10.8.0.1/ai_deploy}"
|
||||
BUNDLE_NAME="${BUNDLE_NAME:-managed-portal-${RELEASE_VERSION}.zip}"
|
||||
INSTALL_ROOT="${INSTALL_ROOT:-/opt/managed-portal-releases}"
|
||||
TARGET_DIR="${TARGET_DIR:-${INSTALL_ROOT}/managed-portal-${RELEASE_VERSION}}"
|
||||
SHARED_ROOT="${SHARED_ROOT:-${INSTALL_ROOT}/shared}"
|
||||
DEFAULT_PEOPLE_FLOW_WEIGHTS_DIR="${SHARED_ROOT}/people_flow_project/weights"
|
||||
|
||||
require_command() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
@@ -14,6 +16,23 @@ require_command() {
|
||||
fi
|
||||
}
|
||||
|
||||
pull_or_use_local() {
|
||||
image="$1"
|
||||
|
||||
if docker pull "$image"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "docker pull failed for $image, checking local image cache" >&2
|
||||
if docker image inspect "$image" >/dev/null 2>&1; then
|
||||
echo "using existing local image $image" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "image unavailable locally after pull failure: $image" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
run_compose() {
|
||||
if command -v docker-compose >/dev/null 2>&1; then
|
||||
docker-compose "$@"
|
||||
@@ -22,6 +41,19 @@ run_compose() {
|
||||
docker compose "$@"
|
||||
}
|
||||
|
||||
dir_has_files() {
|
||||
directory="$1"
|
||||
[ -d "$directory" ] && [ -n "$(find "$directory" -mindepth 1 -print -quit 2>/dev/null)" ]
|
||||
}
|
||||
|
||||
copy_dir_contents() {
|
||||
source_dir="$1"
|
||||
target_dir="$2"
|
||||
|
||||
mkdir -p "$target_dir"
|
||||
cp -R "$source_dir"/. "$target_dir"/
|
||||
}
|
||||
|
||||
download_bundle() {
|
||||
tmp_dir="$1"
|
||||
bundle_zip="$tmp_dir/$BUNDLE_NAME"
|
||||
@@ -58,6 +90,66 @@ build_overlay_image() {
|
||||
printf '%s\n' "$overlay_image"
|
||||
}
|
||||
|
||||
clear_stale_runtime_state() {
|
||||
people_flow_status="$TARGET_DIR/managed/people_flow_project/outputs/rtsp_stream/worker_status.json"
|
||||
|
||||
if [ -f "$people_flow_status" ]; then
|
||||
rm -f "$people_flow_status"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_runtime_directories() {
|
||||
mkdir -p \
|
||||
"$TARGET_DIR/managed/store_dwell_alert/config" \
|
||||
"$TARGET_DIR/managed/store_dwell_alert/data" \
|
||||
"$TARGET_DIR/managed/people_flow_project/config" \
|
||||
"$TARGET_DIR/managed/people_flow_project/outputs/rtsp_stream"
|
||||
}
|
||||
|
||||
find_existing_people_flow_weights() {
|
||||
if dir_has_files "$MANAGED_PEOPLE_FLOW_WEIGHTS_DIR"; then
|
||||
printf '%s\n' "$MANAGED_PEOPLE_FLOW_WEIGHTS_DIR"
|
||||
return 0
|
||||
fi
|
||||
|
||||
for candidate in "$INSTALL_ROOT"/managed-portal-*/managed/people_flow_project/weights; do
|
||||
if [ "$candidate" = "$TARGET_DIR/managed/people_flow_project/weights" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if dir_has_files "$candidate"; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
prepare_people_flow_weights() {
|
||||
bundle_weights_dir="$TARGET_DIR/managed/people_flow_project/weights"
|
||||
source_weights_dir=""
|
||||
|
||||
if dir_has_files "$bundle_weights_dir"; then
|
||||
source_weights_dir="$bundle_weights_dir"
|
||||
echo "seeding shared people-flow weights from bundle" >&2
|
||||
elif source_weights_dir="$(find_existing_people_flow_weights 2>/dev/null)"; then
|
||||
echo "reusing existing people-flow weights from $source_weights_dir" >&2
|
||||
else
|
||||
echo "people-flow weights not found; seed $MANAGED_PEOPLE_FLOW_WEIGHTS_DIR or include managed/people_flow_project/weights in the release zip" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$source_weights_dir" != "$MANAGED_PEOPLE_FLOW_WEIGHTS_DIR" ]; then
|
||||
rm -rf "$MANAGED_PEOPLE_FLOW_WEIGHTS_DIR"
|
||||
copy_dir_contents "$source_weights_dir" "$MANAGED_PEOPLE_FLOW_WEIGHTS_DIR"
|
||||
fi
|
||||
|
||||
rm -rf "$bundle_weights_dir"
|
||||
mkdir -p "$(dirname "$bundle_weights_dir")"
|
||||
ln -s "$MANAGED_PEOPLE_FLOW_WEIGHTS_DIR" "$bundle_weights_dir"
|
||||
}
|
||||
|
||||
require_command curl
|
||||
require_command unzip
|
||||
require_command docker
|
||||
@@ -79,11 +171,18 @@ set -a
|
||||
. "$TARGET_DIR/release-manifest.env"
|
||||
set +a
|
||||
|
||||
MANAGED_PEOPLE_FLOW_WEIGHTS_DIR="${MANAGED_PEOPLE_FLOW_WEIGHTS_DIR:-$DEFAULT_PEOPLE_FLOW_WEIGHTS_DIR}"
|
||||
|
||||
ensure_runtime_directories
|
||||
prepare_people_flow_weights
|
||||
|
||||
clear_stale_runtime_state
|
||||
|
||||
echo "pulling release images"
|
||||
docker pull "$MANAGED_PORTAL_IMAGE"
|
||||
docker pull "$MANAGED_PORTAL_WEB_IMAGE"
|
||||
docker pull "$PEOPLE_FLOW_PROJECT_IMAGE"
|
||||
docker pull "$STORE_DWELL_ALERT_IMAGE"
|
||||
pull_or_use_local "$MANAGED_PORTAL_IMAGE"
|
||||
pull_or_use_local "$MANAGED_PORTAL_WEB_IMAGE"
|
||||
pull_or_use_local "$PEOPLE_FLOW_PROJECT_IMAGE"
|
||||
pull_or_use_local "$STORE_DWELL_ALERT_IMAGE"
|
||||
|
||||
PEOPLE_FLOW_PROJECT_IMAGE="$(build_overlay_image \
|
||||
people-flow-project \
|
||||
@@ -101,6 +200,7 @@ export MANAGED_PORTAL_IMAGE
|
||||
export MANAGED_PORTAL_WEB_IMAGE
|
||||
export PEOPLE_FLOW_PROJECT_IMAGE
|
||||
export STORE_DWELL_ALERT_IMAGE
|
||||
export MANAGED_PEOPLE_FLOW_WEIGHTS_DIR
|
||||
|
||||
cd "$TARGET_DIR/deploy"
|
||||
run_compose \
|
||||
|
||||
98
deploy/package-managed-portal-ota.sh
Normal file
98
deploy/package-managed-portal-ota.sh
Normal file
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
RELEASE_VERSION="${RELEASE_VERSION:?RELEASE_VERSION is required}"
|
||||
RELEASE_MANIFEST_SOURCE="${RELEASE_MANIFEST_SOURCE:?RELEASE_MANIFEST_SOURCE is required}"
|
||||
RELEASE_ENV_SOURCE="${RELEASE_ENV_SOURCE:-$SCRIPT_DIR/managed-portal.env}"
|
||||
OUTPUT_DIR="${OUTPUT_DIR:-$REPO_ROOT/dist/ota}"
|
||||
STAGE_DIR="${OUTPUT_DIR}/managed-portal-${RELEASE_VERSION}"
|
||||
BUNDLE_PATH="${OUTPUT_DIR}/managed-portal-${RELEASE_VERSION}.zip"
|
||||
INSTALLER_PATH="${OUTPUT_DIR}/install-managed-portal-${RELEASE_VERSION}.sh"
|
||||
INCLUDE_WEIGHTS="${INCLUDE_WEIGHTS:-0}"
|
||||
PEOPLE_FLOW_WEIGHTS_SOURCE="${PEOPLE_FLOW_WEIGHTS_SOURCE:-$REPO_ROOT/managed/people_flow_project/weights}"
|
||||
|
||||
require_path() {
|
||||
target="$1"
|
||||
|
||||
if [ ! -e "$target" ]; then
|
||||
echo "missing required path: $target" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
dir_has_files() {
|
||||
directory="$1"
|
||||
[ -d "$directory" ] && [ -n "$(find "$directory" -mindepth 1 -print -quit 2>/dev/null)" ]
|
||||
}
|
||||
|
||||
copy_dir() {
|
||||
source_dir="$1"
|
||||
target_dir="$2"
|
||||
|
||||
mkdir -p "$(dirname "$target_dir")"
|
||||
cp -R "$source_dir" "$target_dir"
|
||||
}
|
||||
|
||||
require_path "$RELEASE_MANIFEST_SOURCE"
|
||||
require_path "$RELEASE_ENV_SOURCE"
|
||||
require_path "$SCRIPT_DIR/docker-compose.ota-release.yml"
|
||||
require_path "$SCRIPT_DIR/Dockerfile.runtime-overlay"
|
||||
require_path "$SCRIPT_DIR/install-managed-portal-ota.sh"
|
||||
require_path "$REPO_ROOT/managed/store_dwell_alert/config"
|
||||
require_path "$REPO_ROOT/managed/people_flow_project/config"
|
||||
|
||||
rm -rf "$STAGE_DIR"
|
||||
mkdir -p "$STAGE_DIR/deploy" "$OUTPUT_DIR"
|
||||
|
||||
cp "$SCRIPT_DIR/docker-compose.ota-release.yml" "$STAGE_DIR/deploy/docker-compose.ota-release.yml"
|
||||
cp "$SCRIPT_DIR/Dockerfile.runtime-overlay" "$STAGE_DIR/deploy/Dockerfile.runtime-overlay"
|
||||
cp "$RELEASE_ENV_SOURCE" "$STAGE_DIR/deploy/managed-portal.release.env"
|
||||
cp "$RELEASE_MANIFEST_SOURCE" "$STAGE_DIR/release-manifest.env"
|
||||
|
||||
copy_dir "$REPO_ROOT/managed/store_dwell_alert/config" "$STAGE_DIR/managed/store_dwell_alert/config"
|
||||
copy_dir "$REPO_ROOT/managed/people_flow_project/config" "$STAGE_DIR/managed/people_flow_project/config"
|
||||
|
||||
if [ "$INCLUDE_WEIGHTS" = "1" ]; then
|
||||
if ! dir_has_files "$PEOPLE_FLOW_WEIGHTS_SOURCE"; then
|
||||
echo "people-flow weights requested but missing under $PEOPLE_FLOW_WEIGHTS_SOURCE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
copy_dir "$PEOPLE_FLOW_WEIGHTS_SOURCE" "$STAGE_DIR/managed/people_flow_project/weights"
|
||||
fi
|
||||
|
||||
python3 - "$OUTPUT_DIR" "$STAGE_DIR" "$BUNDLE_PATH" <<'PY'
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import zipfile
|
||||
|
||||
output_dir = Path(sys.argv[1])
|
||||
stage_dir = Path(sys.argv[2])
|
||||
bundle_path = Path(sys.argv[3])
|
||||
|
||||
if bundle_path.exists():
|
||||
bundle_path.unlink()
|
||||
|
||||
with zipfile.ZipFile(bundle_path, "w", compression=zipfile.ZIP_DEFLATED) as archive:
|
||||
for path in sorted(stage_dir.rglob("*")):
|
||||
arcname = path.relative_to(output_dir)
|
||||
if path.is_dir():
|
||||
if not any(path.iterdir()):
|
||||
archive.writestr(f"{arcname.as_posix().rstrip('/')}/", "")
|
||||
continue
|
||||
archive.write(path, arcname.as_posix())
|
||||
PY
|
||||
|
||||
cp "$SCRIPT_DIR/install-managed-portal-ota.sh" "$INSTALLER_PATH"
|
||||
chmod +x "$INSTALLER_PATH"
|
||||
|
||||
echo "OTA bundle created: $BUNDLE_PATH"
|
||||
echo "Versioned installer created: $INSTALLER_PATH"
|
||||
if [ "$INCLUDE_WEIGHTS" = "1" ]; then
|
||||
echo "Bundle includes managed/people_flow_project/weights"
|
||||
else
|
||||
echo "Bundle excludes managed/people_flow_project/weights; the installer will reuse the shared host weights directory if available"
|
||||
fi
|
||||
Reference in New Issue
Block a user