Files
pzmanager/deploy/get.sh
T
mrleo1nidandClaude Opus 5 c0797be745
CI / test (push) Successful in 21s
Release / release (push) Successful in 27s
Обновление панели из интерфейса и режим обновления в установщике
Установщик стал идемпотентным: на уже настроенной машине он определяет
установленную версию, останавливает службу, меняет бинарник и поднимает её
обратно, не трогая конфиг, миры и учётные записи. Зависимости при
обновлении не переустанавливаются — это лишние минуты apt.

Кнопки «Обновить» и «Перезапустить панель» появились на вкладке
«Настройки» вместе с текущей версией и описанием нового релиза.

Панель работает от непривилегированного пользователя и не может ни
заменить свой бинарник в /usr/local/bin, ни дёрнуть systemctl. Вместо
того чтобы расширять её права, установщик кладёт root-скрипт
pzmanager-admin и разрешает сервисному пользователю через sudoers ровно
эту одну команду. Скрипт сверяет sha256 скачанного бинарника с
checksums.txt релиза. Если правила sudo нет, кнопки не будет — панель
покажет команду для терминала.

Перед обновлением и перезапуском игровой сервер останавливается штатным
quit: иначе его погасил бы systemd уже в момент рестарта, и ожидание
растянулось бы на таймаут остановки.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 19:37:53 +03:00

128 lines
5.1 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Установка или обновление pzmanager до последнего релиза одной командой:
#
# curl -fsSL https://gitea.hsrv.site/mrleo1nid/pzmanager/raw/branch/main/deploy/get.sh | sudo bash
#
# Панель в локальной сети (подставьте адрес своего сервера):
#
# curl -fsSL .../get.sh | sudo PZ_LISTEN=10.10.1.142:8080 bash
#
# Переменные окружения:
# PZ_VERSION какой тег ставить (по умолчанию последний релиз)
# PZ_HOME куда ставить (по умолчанию /opt/pzmanager)
# PZ_USER системный пользователь (по умолчанию pzserver)
# PZ_LISTEN адрес веб-панели (по умолчанию 127.0.0.1:8080)
# PZ_REPO адрес репозитория (по умолчанию этот)
set -euo pipefail
PZ_REPO="${PZ_REPO:-https://gitea.hsrv.site/mrleo1nid/pzmanager}"
PZ_VERSION="${PZ_VERSION:-latest}"
API="${PZ_REPO%/}"
API="${API%.git}"
# Из https://host/owner/repo делаем https://host/api/v1/repos/owner/repo
HOST="$(printf '%s' "$API" | cut -d/ -f1-3)"
PATH_PART="${API#"$HOST"/}"
API="${HOST}/api/v1/repos/${PATH_PART}"
die() { echo "Ошибка: $*" >&2; exit 1; }
[[ $EUID -eq 0 ]] || die "запустите с правами root (sudo)"
for tool in curl sha256sum; do
command -v "$tool" >/dev/null 2>&1 || die "не найдена утилита $tool"
done
case "$(uname -m)" in
x86_64 | amd64) ARCH=amd64 ;;
aarch64 | arm64) ARCH=arm64 ;;
*) die "архитектура $(uname -m) не поддерживается: есть сборки только под amd64 и arm64" ;;
esac
if [[ "$PZ_VERSION" == "latest" ]]; then
RELEASE_URL="${API}/releases/latest"
else
RELEASE_URL="${API}/releases/tags/${PZ_VERSION}"
fi
echo "==> Спрашиваю у Gitea последний релиз"
RELEASE_JSON="$(curl -fsSL "$RELEASE_URL")" \
|| die "не удалось получить релиз с ${RELEASE_URL}"
TAG="$(printf '%s' "$RELEASE_JSON" | grep -o '"tag_name" *: *"[^"]*"' | head -1 | cut -d'"' -f4)"
[[ -n "$TAG" ]] || die "в ответе нет тега — возможно, релизов ещё нет"
# Ссылки на файлы релиза достаём без jq: он есть далеко не на всякой машине.
# Имя файла сравниваем целиком, а не шаблоном: точка в "install.sh" в регулярке
# совпала бы с любым символом.
asset_url() {
printf '%s' "$RELEASE_JSON" \
| grep -o '"browser_download_url" *: *"[^"]*"' \
| cut -d'"' -f4 \
| while read -r url; do
if [[ "${url##*/}" == "$1" ]]; then
printf '%s' "$url"
return
fi
done
}
BIN_NAME="pzmanager-linux-${ARCH}"
BIN_URL="$(asset_url "$BIN_NAME")"
[[ -n "$BIN_URL" ]] || die "в релизе ${TAG} нет файла ${BIN_NAME}"
echo "==> Ставлю ${TAG} (${ARCH})"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
cd "$TMP"
download() {
local name="$1" url required="${2:-required}"
url="$(asset_url "$name")"
if [[ -z "$url" ]]; then
# Явный if, а не "&& return": при set -e короткая форма для обязательного
# файла вернула бы 1 и уронила скрипт молча, не дойдя до die.
if [[ "$required" == "optional" ]]; then
return 1
fi
die "в релизе ${TAG} нет файла ${name}"
fi
curl -fsSL -o "$name" "$url" || die "не удалось скачать ${name}"
}
download "$BIN_NAME"
download install.sh
download pzmanager.service
# Скрипт привилегированных действий появился не сразу: со старым релизом
# ставим без него, панель просто не покажет кнопку обновления.
FILES=("$BIN_NAME" install.sh pzmanager.service)
if download pzmanager-admin.sh optional; then
FILES+=(pzmanager-admin.sh)
fi
# Контрольные суммы обязательны: бинарник ставится с правами root.
download checksums.txt
echo "==> Проверяю контрольные суммы"
: > expected.txt
for name in "${FILES[@]}"; do
# Сравниваем имя целиком через awk: в grep точка из "install.sh" совпала бы
# с любым символом и подтянула чужую строку.
awk -v name="$name" '$NF == name' checksums.txt >> expected.txt
grep -q -F -- "$name" expected.txt \
|| die "в checksums.txt нет строки для ${name}"
done
sha256sum -c expected.txt || die "контрольные суммы не сошлись — файлы повреждены"
mkdir -p deploy
mv install.sh pzmanager.service deploy/
if [[ -f pzmanager-admin.sh ]]; then
mv pzmanager-admin.sh deploy/
fi
mv "$BIN_NAME" pzmanager
chmod +x pzmanager deploy/install.sh
echo "==> Запускаю установщик"
BINARY="$TMP/pzmanager" bash deploy/install.sh