Files
lingniu-vehicle-ingest/vehicle-data-platform/deploy/install-web-release.sh
T
lingniu d6b957dfc8 feat: sync vehicle monitoring and accumulated platform updates
Add monitoring metrics, numeric sorting, report-time display, vehicle number labels, and map layout fixes. Include accumulated mileage, hydrogen, OneOS authentication, open platform, ingestion, deployment, and UI updates.

Exclude temporary files, generated reports, and credentials. Validate frontend 632 tests and production build, API packages, vehicle-map tests, and release installer tests. Gateway suite has one pre-existing date-sensitive identity throttling test failure reproduced on the parent commit.
2026-10-26 18:05:59 +08:00

198 lines
9.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
RELEASE_ID=${1:?release id is required}
ARCHIVE=${2:?web archive is required}
API_BINARY=${3:-}
ONEOS_SCOPE_BINARY=${4:-}
RECONCILIATION_BINARY=${5:-}
OPEN_STAT_BINARY=${6:-}
ALERT_EVALUATOR_BINARY=${7:-}
ALERT_STREAM_EVALUATOR_BINARY=${8:-}
ALERT_NOTIFICATION_DISPATCHER_BINARY=${9:-}
ROOT=${PLATFORM_ROOT:-/opt/lingniu-vehicle-platform}
BASE_URL=${PLATFORM_BASE_URL:-http://127.0.0.1:20300}
SERVICE=${PLATFORM_SERVICE:-lingniu-vehicle-platform}
ALERT_EVALUATOR_SERVICE=${ALERT_EVALUATOR_SERVICE:-lingniu-vehicle-alert-evaluator}
ALERT_STREAM_EVALUATOR_SERVICE=${ALERT_STREAM_EVALUATOR_SERVICE:-lingniu-vehicle-alert-stream-evaluator}
ALERT_NOTIFICATION_DISPATCHER_SERVICE=${ALERT_NOTIFICATION_DISPATCHER_SERVICE:-lingniu-vehicle-alert-notification-dispatcher}
COMPATIBILITY_GENERATIONS=${COMPATIBILITY_GENERATIONS:-3}
RELEASE_HISTORY_LIMIT=${RELEASE_HISTORY_LIMIT:-20}
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
SYSTEMCTL_BIN=${SYSTEMCTL_BIN:-systemctl}
CURL_BIN=${CURL_BIN:-curl}
case "$RELEASE_ID" in
''|*[!A-Za-z0-9._-]*) printf 'invalid release id: %s\n' "$RELEASE_ID" >&2; exit 1 ;;
esac
test -f "$ARCHIVE" || { printf 'web archive is missing: %s\n' "$ARCHIVE" >&2; exit 1; }
if test -n "$API_BINARY"; then
test -f "$API_BINARY" || { printf 'platform API binary is missing: %s\n' "$API_BINARY" >&2; exit 1; }
fi
if test -n "$ONEOS_SCOPE_BINARY"; then
test -f "$ONEOS_SCOPE_BINARY" || { printf 'OneOS scope sync binary is missing: %s\n' "$ONEOS_SCOPE_BINARY" >&2; exit 1; }
fi
if test -n "$RECONCILIATION_BINARY"; then
test -f "$RECONCILIATION_BINARY" || { printf 'reconciliation evaluator binary is missing: %s\n' "$RECONCILIATION_BINARY" >&2; exit 1; }
fi
if test -n "$OPEN_STAT_BINARY"; then
test -f "$OPEN_STAT_BINARY" || { printf 'open platform statistics binary is missing: %s\n' "$OPEN_STAT_BINARY" >&2; exit 1; }
fi
if test -n "$ALERT_EVALUATOR_BINARY"; then
test -f "$ALERT_EVALUATOR_BINARY" || { printf 'alert evaluator binary is missing: %s\n' "$ALERT_EVALUATOR_BINARY" >&2; exit 1; }
fi
if test -n "$ALERT_STREAM_EVALUATOR_BINARY"; then
test -f "$ALERT_STREAM_EVALUATOR_BINARY" || { printf 'alert stream evaluator binary is missing: %s\n' "$ALERT_STREAM_EVALUATOR_BINARY" >&2; exit 1; }
fi
if test -n "$ALERT_NOTIFICATION_DISPATCHER_BINARY"; then
test -f "$ALERT_NOTIFICATION_DISPATCHER_BINARY" || { printf 'alert notification dispatcher binary is missing: %s\n' "$ALERT_NOTIFICATION_DISPATCHER_BINARY" >&2; exit 1; }
fi
test -x "$SCRIPT_DIR/prepare-web-release-tree.sh" || { printf 'release tree helper is missing\n' >&2; exit 1; }
test -f "$SCRIPT_DIR/prune-release-history.py" || { printf 'release pruning helper is missing\n' >&2; exit 1; }
test -f "$SCRIPT_DIR/verify-customer-demo.py" || { printf 'customer demo gate is missing\n' >&2; exit 1; }
test -f "$SCRIPT_DIR/import-source-providers.py" || { printf 'source provider import helper is missing\n' >&2; exit 1; }
old=$(readlink -f "$ROOT/current")
next="$ROOT/releases/$RELEASE_ID"
env_file="$ROOT/env/platform.env"
old_release=$(sed -n 's/^PLATFORM_RELEASE=//p' "$env_file" | tail -1)
switched=false
set_release_env() {
python3 - "$env_file" "$1" <<'PY'
from __future__ import print_function
import io
import sys
path, release = sys.argv[1], sys.argv[2]
with io.open(path, 'r', encoding='utf-8') as handle:
lines = handle.read().splitlines()
updated = []
found = False
for line in lines:
if line.startswith('PLATFORM_RELEASE='):
updated.append('PLATFORM_RELEASE=' + release)
found = True
else:
updated.append(line)
if not found:
updated.append('PLATFORM_RELEASE=' + release)
with io.open(path, 'w', encoding='utf-8') as handle:
handle.write('\n'.join(updated) + '\n')
PY
}
rollback() {
local status=$?
trap - ERR
set +e
if test "$switched" = true; then
ln -s "$old" "$ROOT/current.rollback"
python3 -c 'import os,sys; os.replace(sys.argv[1], sys.argv[2])' "$ROOT/current.rollback" "$ROOT/current"
set_release_env "$old_release"
"$SYSTEMCTL_BIN" restart "$SERVICE"
test -z "$ALERT_EVALUATOR_BINARY" || "$SYSTEMCTL_BIN" restart "$ALERT_EVALUATOR_SERVICE"
test -z "$ALERT_STREAM_EVALUATOR_BINARY" || "$SYSTEMCTL_BIN" restart "$ALERT_STREAM_EVALUATOR_SERVICE"
test -z "$ALERT_NOTIFICATION_DISPATCHER_BINARY" || "$SYSTEMCTL_BIN" restart "$ALERT_NOTIFICATION_DISPATCHER_SERVICE"
fi
printf 'web release install failed; current restored to %s\n' "$old" >&2
exit "$status"
}
trap rollback ERR
test ! -e "$next" || { printf 'release already exists: %s\n' "$next" >&2; exit 1; }
mkdir -p "$next/web"
for runtime_file in platform-api customer-hydrogen-worker alert-evaluator alert-stream-evaluator alert-notification-dispatcher platform-migrate oneos-scope-sync reconciliation-evaluator alert-benchmark open-platform-stat; do
if test -f "$old/$runtime_file"; then
cp "$old/$runtime_file" "$next/$runtime_file"
fi
done
test -f "$next/platform-api" || { printf 'current release is missing platform-api\n' >&2; exit 1; }
if test -n "$API_BINARY"; then
cp "$API_BINARY" "$next/platform-api"
fi
if test -n "$ONEOS_SCOPE_BINARY"; then
cp "$ONEOS_SCOPE_BINARY" "$next/oneos-scope-sync"
fi
if test -n "$RECONCILIATION_BINARY"; then
cp "$RECONCILIATION_BINARY" "$next/reconciliation-evaluator"
fi
if test -n "$OPEN_STAT_BINARY"; then
cp "$OPEN_STAT_BINARY" "$next/open-platform-stat"
fi
if test -n "$ALERT_EVALUATOR_BINARY"; then
cp "$ALERT_EVALUATOR_BINARY" "$next/alert-evaluator"
fi
if test -n "$ALERT_STREAM_EVALUATOR_BINARY"; then
cp "$ALERT_STREAM_EVALUATOR_BINARY" "$next/alert-stream-evaluator"
fi
if test -n "$ALERT_NOTIFICATION_DISPATCHER_BINARY"; then
cp "$ALERT_NOTIFICATION_DISPATCHER_BINARY" "$next/alert-notification-dispatcher"
fi
if test -f "$old/lingniu-vehicle-platform.service"; then
cp "$old/lingniu-vehicle-platform.service" "$next/lingniu-vehicle-platform.service"
fi
cp -a "$old/deploy" "$next/deploy"
if test -d "$SCRIPT_DIR/migrations"; then
mkdir -p "$next/deploy/migrations"
cp "$SCRIPT_DIR"/migrations/*.sql "$next/deploy/migrations/"
fi
if test -d "$SCRIPT_DIR/systemd"; then
mkdir -p "$next/deploy/systemd"
cp "$SCRIPT_DIR"/systemd/* "$next/deploy/systemd/"
fi
cp "$SCRIPT_DIR/prepare-web-release-tree.sh" "$next/deploy/prepare-web-release-tree.sh"
cp "$SCRIPT_DIR/prune-release-history.py" "$next/deploy/prune-release-history.py"
cp "$SCRIPT_DIR/verify-customer-demo.py" "$next/deploy/verify-customer-demo.py"
cp "$SCRIPT_DIR/import-source-providers.py" "$next/deploy/import-source-providers.py"
cp "$0" "$next/deploy/install-web-release.sh"
chmod +x "$next/platform-api" "$next/deploy/"*.sh "$next/deploy/prune-release-history.py" "$next/deploy/verify-customer-demo.py" "$next/deploy/import-source-providers.py"
for runtime_file in customer-hydrogen-worker alert-evaluator alert-stream-evaluator alert-notification-dispatcher platform-migrate oneos-scope-sync reconciliation-evaluator alert-benchmark open-platform-stat; do
test ! -f "$next/$runtime_file" || chmod +x "$next/$runtime_file"
done
while IFS= read -r member; do
case "$member" in
/*|../*|*/../*|*/..) printf 'web archive contains unsafe path: %s\n' "$member" >&2; exit 1 ;;
esac
done < <(tar -tzf "$ARCHIVE")
tar --no-same-owner -xzf "$ARCHIVE" -C "$next/web"
find "$next/web" -type f -name '._*' -delete
"$next/deploy/prepare-web-release-tree.sh" "$next/web" "$old/web" "$COMPATIBILITY_GENERATIONS"
ln -s "$next" "$ROOT/current.next"
python3 -c 'import os,sys; os.replace(sys.argv[1], sys.argv[2])' "$ROOT/current.next" "$ROOT/current"
switched=true
set_release_env "$RELEASE_ID"
"$SYSTEMCTL_BIN" restart "$SERVICE"
test -z "$ALERT_EVALUATOR_BINARY" || "$SYSTEMCTL_BIN" restart "$ALERT_EVALUATOR_SERVICE"
test -z "$ALERT_STREAM_EVALUATOR_BINARY" || "$SYSTEMCTL_BIN" restart "$ALERT_STREAM_EVALUATOR_SERVICE"
test -z "$ALERT_NOTIFICATION_DISPATCHER_BINARY" || "$SYSTEMCTL_BIN" restart "$ALERT_NOTIFICATION_DISPATCHER_SERVICE"
ready=false
for _ in $(seq 1 15); do
if "$SYSTEMCTL_BIN" is-active --quiet "$SERVICE" && "$CURL_BIN" -fsS "$BASE_URL/" >/dev/null; then ready=true; break; fi
sleep 1
done
test "$ready" = true || { printf 'service did not become ready\n' >&2; exit 1; }
verify_bin=${VERIFY_WEB_RELEASE_BIN:-$next/deploy/verify-web-release.sh}
"$verify_bin" "$next/web" "$BASE_URL" "$next/web/.compatibility-assets"
if test -f "$ROOT/env/access-tokens.env"; then
set -a
# shellcheck disable=SC1090
. "$ROOT/env/access-tokens.env"
set +a
test -n "${VIEWER_TOKEN:-}" || { printf 'viewer token is unavailable for health verification\n' >&2; exit 1; }
health=$("$CURL_BIN" -fsS -H "Authorization: Bearer $VIEWER_TOKEN" "$BASE_URL/api/ops/health")
printf '%s' "$health" | python3 -c 'import json,sys; value=(((json.load(sys.stdin).get("data") or {}).get("runtime") or {}).get("platformRelease")); sys.exit(0 if value == sys.argv[1] else 1)' "$RELEASE_ID"
fi
prune_bin=${PRUNE_RELEASE_BIN:-$next/deploy/prune-release-history.py}
python3 "$prune_bin" "$ROOT" "$RELEASE_HISTORY_LIMIT" "$next" "$old"
rm -f "$ARCHIVE"
switched=false
trap - ERR
printf 'web_release_install=ok release=%s previous=%s\n' "$RELEASE_ID" "$(basename "$old")"