122 lines
3.9 KiB
Bash
Executable File
122 lines
3.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
RELEASE_ID=${1:?release id is required}
|
|
WEB_ARCHIVE=${2:?open portal web archive is required}
|
|
API_BINARY=${3:?open platform API binary is required}
|
|
ROOT=${OPEN_PLATFORM_ROOT:-/opt/lingniu-vehicle-open-platform}
|
|
BASE_URL=${OPEN_PLATFORM_BASE_URL:-http://127.0.0.1:20310}
|
|
SERVICE=${OPEN_PLATFORM_SERVICE:-lingniu-vehicle-open-platform}
|
|
SYSTEMCTL_BIN=${SYSTEMCTL_BIN:-systemctl}
|
|
CURL_BIN=${CURL_BIN:-curl}
|
|
RELEASE_HISTORY_LIMIT=${RELEASE_HISTORY_LIMIT:-12}
|
|
|
|
case "$RELEASE_ID" in
|
|
''|*[!A-Za-z0-9._-]*) printf 'invalid release id: %s\n' "$RELEASE_ID" >&2; exit 1 ;;
|
|
esac
|
|
test -f "$WEB_ARCHIVE" || { printf 'open portal archive is missing\n' >&2; exit 1; }
|
|
test -f "$API_BINARY" || { printf 'open platform API binary is missing\n' >&2; exit 1; }
|
|
|
|
old=
|
|
if test -L "$ROOT/current"; then
|
|
old=$(readlink -f "$ROOT/current")
|
|
fi
|
|
next="$ROOT/releases/$RELEASE_ID"
|
|
env_file="$ROOT/env/open-platform.env"
|
|
switched=false
|
|
|
|
set_release_env() {
|
|
python3 - "$env_file" "$1" <<'PY'
|
|
import pathlib
|
|
import sys
|
|
path = pathlib.Path(sys.argv[1])
|
|
release = sys.argv[2]
|
|
lines = path.read_text(encoding="utf-8").splitlines() if path.exists() else []
|
|
updated = []
|
|
found = False
|
|
for line in lines:
|
|
if line.startswith("OPEN_PLATFORM_RELEASE="):
|
|
updated.append("OPEN_PLATFORM_RELEASE=" + release)
|
|
found = True
|
|
else:
|
|
updated.append(line)
|
|
if not found:
|
|
updated.append("OPEN_PLATFORM_RELEASE=" + release)
|
|
path.write_text("\n".join(updated) + "\n", encoding="utf-8")
|
|
PY
|
|
chmod 0600 "$env_file"
|
|
}
|
|
|
|
rollback() {
|
|
status=$?
|
|
trap - ERR
|
|
set +e
|
|
if test "$switched" = true && test -n "$old"; 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 "$(basename "$old")"
|
|
"$SYSTEMCTL_BIN" restart "$SERVICE"
|
|
elif test "$switched" = true; then
|
|
"$SYSTEMCTL_BIN" stop "$SERVICE"
|
|
rm -f "$ROOT/current"
|
|
set_release_env ""
|
|
fi
|
|
printf 'open platform release failed; current restored to %s\n' "${old:-none}" >&2
|
|
exit "$status"
|
|
}
|
|
trap rollback ERR
|
|
|
|
test ! -e "$next" || { printf 'release already exists: %s\n' "$next" >&2; exit 1; }
|
|
mkdir -p "$next/web" "$ROOT/env"
|
|
cp "$API_BINARY" "$next/open-platform-api"
|
|
chmod 0755 "$next/open-platform-api"
|
|
|
|
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 "$WEB_ARCHIVE")
|
|
tar --no-same-owner -xzf "$WEB_ARCHIVE" -C "$next/web"
|
|
test -f "$next/web/index.html" || { printf 'open portal index is missing\n' >&2; exit 1; }
|
|
|
|
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"
|
|
|
|
ready=false
|
|
for _ in $(seq 1 20); do
|
|
if "$SYSTEMCTL_BIN" is-active --quiet "$SERVICE" && "$CURL_BIN" -fsS "$BASE_URL/healthz" >/dev/null; then
|
|
ready=true
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
test "$ready" = true || { printf 'open platform service did not become ready\n' >&2; exit 1; }
|
|
"$CURL_BIN" -fsS "$BASE_URL/" | grep -q '<div id="root"></div>'
|
|
"$CURL_BIN" -fsS "$BASE_URL/open-api/openapi.yaml" | grep -q '^openapi: 3.0.3'
|
|
"$CURL_BIN" -fsS "$BASE_URL/portal-api/catalog" | grep -q 'daily_hydrogen'
|
|
|
|
python3 - "$ROOT" "$RELEASE_HISTORY_LIMIT" "$next" "$old" <<'PY'
|
|
import pathlib
|
|
import shutil
|
|
import sys
|
|
root = pathlib.Path(sys.argv[1])
|
|
limit = int(sys.argv[2])
|
|
protected = {pathlib.Path(value).resolve() for value in sys.argv[3:] if value}
|
|
releases = sorted(
|
|
(p for p in (root / "releases").iterdir() if p.is_dir()),
|
|
key=lambda p: p.stat().st_mtime,
|
|
reverse=True,
|
|
)
|
|
for path in releases[limit:]:
|
|
if path.resolve() not in protected:
|
|
shutil.rmtree(path)
|
|
PY
|
|
|
|
switched=false
|
|
trap - ERR
|
|
printf 'open_platform_release_install=ok release=%s previous=%s\n' "$RELEASE_ID" "${old##*/}"
|