初始化奇妙小屏幕控制器项目
This commit is contained in:
@@ -0,0 +1,262 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "update_walnutpi.sh must run as root" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$(uname -m)" != "aarch64" ]; then
|
||||
echo "this update is only for the WalnutPi AArch64 host" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SOURCE_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
PROJECT_ROOT=$(CDPATH= cd -- "$SOURCE_ROOT/.." && pwd)
|
||||
WHEELHOUSE=${WHEELHOUSE:-$PROJECT_ROOT/发布更新相关/其他依赖/aarch64-py311}
|
||||
FRPC_BUNDLE=${FRPC_BUNDLE:-$PROJECT_ROOT/发布更新相关/其他依赖/frp/0.71.0/linux-arm64}
|
||||
MANIFEST=$WHEELHOUSE/SHA256SUMS
|
||||
TARGET=/opt/matrix-screen-controller
|
||||
DATA_ROOT=/var/lib/matrix-screen-controller
|
||||
UNIT=/etc/systemd/system/matrix-screen-controller.service
|
||||
OTA_UNIT=/etc/systemd/system/matrix-screen-controller-ota.service
|
||||
SERVICE=matrix-screen-controller.service
|
||||
SOFTWARE_VERSION=$(tr -d '\r\n' < "$SOURCE_ROOT/VERSION")
|
||||
RELEASE_ID=${RELEASE_ID:-$SOFTWARE_VERSION-$(date -u +%Y%m%dT%H%M%SZ)}
|
||||
RELEASES=/opt/matrix-screen-controller.releases
|
||||
|
||||
case "$RELEASE_ID" in
|
||||
*[!A-Za-z0-9._-]*|'')
|
||||
echo "RELEASE_ID may contain only letters, digits, dot, underscore, and dash" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
STAGING=/opt/matrix-screen-controller.stage.$RELEASE_ID
|
||||
BACKUP=/opt/matrix-screen-controller.rollback.$RELEASE_ID
|
||||
NEW_RELEASE=$RELEASES/$RELEASE_ID
|
||||
DATA_CANDIDATE=/var/lib/matrix-screen-controller.update.$RELEASE_ID
|
||||
DATA_BACKUP=/var/lib/matrix-screen-controller.rollback.$RELEASE_ID
|
||||
MIGRATION_RUNTIME=/run/matrix-screen-controller-update.$RELEASE_ID.migration
|
||||
STATUS_FILE=/run/matrix-screen-controller-update.$RELEASE_ID.status
|
||||
TEST_DATA=/run/matrix-screen-controller-update.$RELEASE_ID.test-data
|
||||
TEST_RUNTIME=/run/matrix-screen-controller-update.$RELEASE_ID.test-runtime
|
||||
FRPC_BACKUP=/run/matrix-screen-controller-update.$RELEASE_ID.frpc-backup
|
||||
SWAPPED=0
|
||||
DATA_BACKED_UP=0
|
||||
SUCCESS=0
|
||||
SERVICE_STOPPED=0
|
||||
FRPC_CHANGED=0
|
||||
FRPC_WAS_ACTIVE=0
|
||||
FRPC_WAS_ENABLED=0
|
||||
PREVIOUS_LINK=
|
||||
|
||||
cleanup_paths() {
|
||||
for path in "$STAGING" "$DATA_CANDIDATE" "$MIGRATION_RUNTIME" "$STATUS_FILE" "$TEST_DATA" "$TEST_RUNTIME" "$FRPC_BACKUP"; do
|
||||
case "$path" in
|
||||
/opt/matrix-screen-controller.stage.$RELEASE_ID|/var/lib/matrix-screen-controller.update.$RELEASE_ID|/run/matrix-screen-controller-update.$RELEASE_ID.migration|/run/matrix-screen-controller-update.$RELEASE_ID.status|/run/matrix-screen-controller-update.$RELEASE_ID.test-data|/run/matrix-screen-controller-update.$RELEASE_ID.test-runtime|/run/matrix-screen-controller-update.$RELEASE_ID.frpc-backup)
|
||||
if [ -d "$path" ]; then rm -rf -- "$path"; else rm -f -- "$path"; fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
rollback() {
|
||||
if [ "$SUCCESS" -eq 1 ]; then
|
||||
return
|
||||
fi
|
||||
if [ "$SWAPPED" -eq 1 ] || [ "$DATA_BACKED_UP" -eq 1 ]; then
|
||||
echo "new release failed; restoring previous program and persistent data" >&2
|
||||
fi
|
||||
if [ "$SERVICE_STOPPED" -eq 1 ] || [ "$SWAPPED" -eq 1 ] || [ "$DATA_BACKED_UP" -eq 1 ]; then
|
||||
systemctl stop "$SERVICE" || true
|
||||
fi
|
||||
if [ "$DATA_BACKED_UP" -eq 1 ]; then
|
||||
FAILED_DATA=/var/lib/matrix-screen-controller.failed.$RELEASE_ID
|
||||
if [ -e "$DATA_ROOT" ] && [ ! -e "$FAILED_DATA" ]; then mv -- "$DATA_ROOT" "$FAILED_DATA"; fi
|
||||
if [ -d "$DATA_BACKUP" ]; then mv -- "$DATA_BACKUP" "$DATA_ROOT"; fi
|
||||
case "$FAILED_DATA" in
|
||||
/var/lib/matrix-screen-controller.failed.$RELEASE_ID)
|
||||
if [ -d "$FAILED_DATA" ]; then rm -rf -- "$FAILED_DATA"; fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if [ "$FRPC_CHANGED" -eq 1 ]; then
|
||||
systemctl stop matrix-screen-frpc.service 2>/dev/null || true
|
||||
if [ -f "$FRPC_BACKUP/frpc" ]; then install -m 0755 "$FRPC_BACKUP/frpc" /usr/local/bin/frpc; else rm -f -- /usr/local/bin/frpc; fi
|
||||
if [ -f "$FRPC_BACKUP/unit" ]; then install -m 0644 "$FRPC_BACKUP/unit" /etc/systemd/system/matrix-screen-frpc.service; else rm -f -- /etc/systemd/system/matrix-screen-frpc.service; fi
|
||||
if [ -f "$FRPC_BACKUP/dropin" ]; then
|
||||
install -d -m 0755 /etc/systemd/system/matrix-screen-frpc.service.d
|
||||
install -m 0644 "$FRPC_BACKUP/dropin" /etc/systemd/system/matrix-screen-frpc.service.d/user.conf
|
||||
else
|
||||
rm -f -- /etc/systemd/system/matrix-screen-frpc.service.d/user.conf
|
||||
fi
|
||||
systemctl daemon-reload
|
||||
if [ "$FRPC_WAS_ENABLED" -eq 1 ]; then systemctl enable matrix-screen-frpc.service >/dev/null; else systemctl disable matrix-screen-frpc.service >/dev/null 2>&1 || true; fi
|
||||
if [ "$FRPC_WAS_ACTIVE" -eq 1 ]; then systemctl start matrix-screen-frpc.service || true; fi
|
||||
fi
|
||||
if [ "$SWAPPED" -eq 1 ]; then
|
||||
if [ -L "$TARGET" ]; then
|
||||
rm -f -- "$TARGET"
|
||||
elif [ -d "$TARGET" ]; then
|
||||
FAILED=/opt/matrix-screen-controller.failed.$RELEASE_ID
|
||||
if [ ! -e "$FAILED" ]; then mv -- "$TARGET" "$FAILED"; fi
|
||||
fi
|
||||
if [ -d "$BACKUP" ]; then
|
||||
mv -- "$BACKUP" "$TARGET"
|
||||
elif [ -n "$PREVIOUS_LINK" ]; then
|
||||
ln -s -- "$PREVIOUS_LINK" "$TARGET"
|
||||
fi
|
||||
rm -rf -- "$NEW_RELEASE"
|
||||
install -m 0644 "$TARGET/systemd/matrix-screen-controller.service" "$UNIT"
|
||||
install -m 0644 "$TARGET/systemd/matrix-screen-controller-ota.service" "$OTA_UNIT"
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
if [ "$SERVICE_STOPPED" -eq 1 ] || [ "$SWAPPED" -eq 1 ] || [ "$DATA_BACKED_UP" -eq 1 ]; then
|
||||
systemctl start "$SERVICE" || true
|
||||
fi
|
||||
}
|
||||
|
||||
finish() {
|
||||
code=$?
|
||||
trap - EXIT HUP INT TERM
|
||||
rollback
|
||||
cleanup_paths
|
||||
exit "$code"
|
||||
}
|
||||
trap finish EXIT HUP INT TERM
|
||||
|
||||
if [ ! -f "$MANIFEST" ]; then
|
||||
echo "offline wheel manifest missing: $MANIFEST" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -d "$TARGET" ]; then
|
||||
echo "existing production target missing: $TARGET" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -d "$DATA_ROOT" ]; then
|
||||
echo "existing persistent data root missing: $DATA_ROOT" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -e "$STAGING" ] || [ -e "$BACKUP" ] || [ -e "$NEW_RELEASE" ] || [ -e "$DATA_CANDIDATE" ] || [ -e "$DATA_BACKUP" ]; then
|
||||
echo "release staging or rollback path already exists for $RELEASE_ID" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
(cd "$WHEELHOUSE" && sha256sum -c SHA256SUMS)
|
||||
mkdir -m 0750 -- "$STAGING"
|
||||
cp -a "$SOURCE_ROOT/." "$STAGING/"
|
||||
python3 -m venv "$STAGING/.venv"
|
||||
"$STAGING/.venv/bin/pip" install \
|
||||
--no-index \
|
||||
--find-links "$WHEELHOUSE" \
|
||||
-r "$STAGING/requirements-dev.txt"
|
||||
make -C "$STAGING/app/display/native" clean all test
|
||||
"$STAGING/.venv/bin/python" -m compileall -q "$STAGING/app"
|
||||
mkdir -p -- "$TEST_DATA" "$TEST_RUNTIME/tmp"
|
||||
(cd "$STAGING" && \
|
||||
MATRIX_DRIVER=mock \
|
||||
MATRIX_TEST_ROOT="$TEST_DATA" \
|
||||
MATRIX_DATA_DIR="$TEST_DATA" \
|
||||
MATRIX_RUNTIME_DIR="$TEST_RUNTIME" \
|
||||
MATRIX_SOURCE_ONLY_UPDATE_TESTS=1 \
|
||||
TMPDIR="$TEST_RUNTIME/tmp" \
|
||||
TEMP="$TEST_RUNTIME/tmp" \
|
||||
TMP="$TEST_RUNTIME/tmp" \
|
||||
"$STAGING/.venv/bin/python" -m pytest -p no:cacheprovider -q \
|
||||
--basetemp="$TEST_RUNTIME/pytest" --tb=short --disable-warnings)
|
||||
"$STAGING/.venv/bin/python" "$STAGING/scripts/dedicated_host.py" check
|
||||
|
||||
systemctl stop "$SERVICE"
|
||||
SERVICE_STOPPED=1
|
||||
cp -a -- "$DATA_ROOT" "$DATA_CANDIDATE"
|
||||
PYTHONPATH="$STAGING" "$STAGING/.venv/bin/python" -m scripts.prepare_data_root \
|
||||
--data-root "$DATA_CANDIDATE" \
|
||||
--runtime-root "$MIGRATION_RUNTIME"
|
||||
mkdir -p -- "$RELEASES"
|
||||
if [ -L "$TARGET" ]; then
|
||||
PREVIOUS_LINK=$(readlink "$TARGET")
|
||||
rm -f -- "$TARGET"
|
||||
SWAPPED=1
|
||||
else
|
||||
mv -- "$TARGET" "$BACKUP"
|
||||
SWAPPED=1
|
||||
fi
|
||||
mv -- "$STAGING" "$NEW_RELEASE"
|
||||
ln -s -- "$NEW_RELEASE" "$TARGET"
|
||||
mv -- "$DATA_ROOT" "$DATA_BACKUP"
|
||||
DATA_BACKED_UP=1
|
||||
mv -- "$DATA_CANDIDATE" "$DATA_ROOT"
|
||||
install -m 0644 "$TARGET/systemd/matrix-screen-controller.service" "$UNIT"
|
||||
install -m 0644 "$TARGET/systemd/matrix-screen-controller-ota.service" "$OTA_UNIT"
|
||||
install -d -m 0700 "$FRPC_BACKUP"
|
||||
systemctl is-active --quiet matrix-screen-frpc.service 2>/dev/null && FRPC_WAS_ACTIVE=1 || true
|
||||
systemctl is-enabled --quiet matrix-screen-frpc.service 2>/dev/null && FRPC_WAS_ENABLED=1 || true
|
||||
if [ -f /usr/local/bin/frpc ]; then cp -p -- /usr/local/bin/frpc "$FRPC_BACKUP/frpc"; fi
|
||||
if [ -f /etc/systemd/system/matrix-screen-frpc.service ]; then cp -p -- /etc/systemd/system/matrix-screen-frpc.service "$FRPC_BACKUP/unit"; fi
|
||||
if [ -f /etc/systemd/system/matrix-screen-frpc.service.d/user.conf ]; then cp -p -- /etc/systemd/system/matrix-screen-frpc.service.d/user.conf "$FRPC_BACKUP/dropin"; fi
|
||||
FRPC_CHANGED=1
|
||||
FRPC_BUNDLE="$FRPC_BUNDLE" /bin/sh "$TARGET/scripts/install_frpc_system.sh"
|
||||
systemctl daemon-reload
|
||||
systemctl start "$SERVICE"
|
||||
|
||||
check_health() {
|
||||
attempt=0
|
||||
while [ "$attempt" -lt 30 ]; do
|
||||
if curl --fail --silent http://127.0.0.1:8080/api/status >"$STATUS_FILE"; then
|
||||
if "$TARGET/.venv/bin/python" - "$STATUS_FILE" <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
status = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
||||
screen = status.get("screen", {})
|
||||
driver = screen.get("driver_status") or {}
|
||||
expected_version = Path("/opt/matrix-screen-controller/VERSION").read_text(encoding="utf-8").strip()
|
||||
assert status.get("service", {}).get("software_version") == expected_version
|
||||
assert screen.get("driver") == "walnutpi-h618-hub75"
|
||||
assert screen.get("hardware_mapping") == "walnutpi-pi-bank-pwm-oe-v2"
|
||||
assert driver.get("oe_timing_backend") == "h618-pwm4"
|
||||
assert driver.get("oe_pulse_faults") == 0
|
||||
PY
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
sleep 1
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
if ! check_health; then
|
||||
echo "new release did not pass the production health check" >&2
|
||||
exit 1
|
||||
fi
|
||||
systemctl restart "$SERVICE"
|
||||
if ! check_health; then
|
||||
echo "new release did not pass the restart health check" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SUCCESS=1
|
||||
SWAPPED=0
|
||||
DATA_BACKED_UP=0
|
||||
SERVICE_STOPPED=0
|
||||
FRPC_CHANGED=0
|
||||
case "$BACKUP" in
|
||||
/opt/matrix-screen-controller.rollback.$RELEASE_ID)
|
||||
rm -rf -- "$BACKUP"
|
||||
;;
|
||||
esac
|
||||
case "$DATA_BACKUP" in
|
||||
/var/lib/matrix-screen-controller.rollback.$RELEASE_ID)
|
||||
rm -rf -- "$DATA_BACKUP"
|
||||
;;
|
||||
esac
|
||||
case "$PREVIOUS_LINK" in
|
||||
/opt/matrix-screen-controller.releases/*)
|
||||
if [ "$PREVIOUS_LINK" != "$NEW_RELEASE" ]; then rm -rf -- "$PREVIOUS_LINK"; fi
|
||||
;;
|
||||
esac
|
||||
systemctl --no-pager --full status "$SERVICE"
|
||||
echo "WalnutPi Matrix Screen Controller updated successfully: $RELEASE_ID"
|
||||
Reference in New Issue
Block a user