Код настройки печатает установщик, появилось удаление панели
Менеджер кладёт код первичной настройки в data/setup-token, установщик
дожидается файла и печатает код последним сообщением — за ним больше не
нужно идти в journalctl. Файл удаляется, как только создан администратор,
и не появляется на уже настроенной панели.
Установщик научился снимать панель с машины: --uninstall убирает службу,
бинарники и правило sudo, --purge вдобавок сносит каталог с мирами,
бэкапами и учётками. Данные удаляются только по явному подтверждению, а
каталог сносится, лишь если похож на установку панели: опечатка в PZ_HOME
не должна стоить чужих файлов. Копия установщика теперь лежит в
/opt/pzmanager, так что удалять можно без похода в репозиторий.
get.sh пробрасывает аргументы установщику и в режиме удаления не качает
бинарник. Заодно исправлена подстановка пустого массива аргументов:
"${ARGS[@]:-}" разворачивается в один пустой аргумент, из-за чего обычная
установка спотыкалась бы о него.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+41
-9
@@ -7,6 +7,15 @@
|
||||
#
|
||||
# curl -fsSL .../get.sh | sudo PZ_LISTEN=10.10.1.142:8080 bash
|
||||
#
|
||||
# Удаление (аргументы уходят установщику как есть):
|
||||
#
|
||||
# curl -fsSL .../get.sh | sudo bash -s -- --uninstall
|
||||
# curl -fsSL .../get.sh | sudo bash -s -- --uninstall --purge
|
||||
#
|
||||
# Если панель уже установлена, то же самое умеет её локальная копия:
|
||||
#
|
||||
# sudo /opt/pzmanager/install.sh --uninstall --purge
|
||||
#
|
||||
# Переменные окружения:
|
||||
# PZ_VERSION какой тег ставить (по умолчанию последний релиз)
|
||||
# PZ_HOME куда ставить (по умолчанию /opt/pzmanager)
|
||||
@@ -29,6 +38,19 @@ die() { echo "Ошибка: $*" >&2; exit 1; }
|
||||
|
||||
[[ $EUID -eq 0 ]] || die "запустите с правами root (sudo)"
|
||||
|
||||
# Аргументы уходят установщику. Для удаления бинарник качать незачем — это
|
||||
# лишние восемь мегабайт ради скрипта, который его же и сотрёт.
|
||||
ARGS=("$@")
|
||||
NEED_BINARY=1
|
||||
# Идиома ${ARGS[@]+...} нужна из-за set -u: обычная "${ARGS[@]:-}" на пустом
|
||||
# массиве разворачивается в один пустой аргумент, и установщик спотыкается о
|
||||
# него при обычной установке.
|
||||
for arg in ${ARGS[@]+"${ARGS[@]}"}; do
|
||||
case "$arg" in
|
||||
--uninstall | --remove | --purge) NEED_BINARY=0 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
for tool in curl sha256sum; do
|
||||
command -v "$tool" >/dev/null 2>&1 || die "не найдена утилита $tool"
|
||||
done
|
||||
@@ -68,10 +90,13 @@ asset_url() {
|
||||
}
|
||||
|
||||
BIN_NAME="pzmanager-linux-${ARCH}"
|
||||
BIN_URL="$(asset_url "$BIN_NAME")"
|
||||
[[ -n "$BIN_URL" ]] || die "в релизе ${TAG} нет файла ${BIN_NAME}"
|
||||
|
||||
echo "==> Ставлю ${TAG} (${ARCH})"
|
||||
if [[ "$NEED_BINARY" == "1" ]]; then
|
||||
BIN_URL="$(asset_url "$BIN_NAME")"
|
||||
[[ -n "$BIN_URL" ]] || die "в релизе ${TAG} нет файла ${BIN_NAME}"
|
||||
echo "==> Ставлю ${TAG} (${ARCH})"
|
||||
else
|
||||
echo "==> Беру установщик из ${TAG} для удаления"
|
||||
fi
|
||||
|
||||
TMP="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
@@ -91,13 +116,17 @@ download() {
|
||||
curl -fsSL -o "$name" "$url" || die "не удалось скачать ${name}"
|
||||
}
|
||||
|
||||
download "$BIN_NAME"
|
||||
download install.sh
|
||||
download pzmanager.service
|
||||
FILES=(install.sh pzmanager.service)
|
||||
|
||||
if [[ "$NEED_BINARY" == "1" ]]; then
|
||||
download "$BIN_NAME"
|
||||
FILES+=("$BIN_NAME")
|
||||
fi
|
||||
|
||||
# Скрипт привилегированных действий появился не сразу: со старым релизом
|
||||
# ставим без него, панель просто не покажет кнопку обновления.
|
||||
FILES=("$BIN_NAME" install.sh pzmanager.service)
|
||||
if download pzmanager-admin.sh optional; then
|
||||
FILES+=(pzmanager-admin.sh)
|
||||
fi
|
||||
@@ -120,8 +149,11 @@ 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
|
||||
chmod +x deploy/install.sh
|
||||
if [[ "$NEED_BINARY" == "1" ]]; then
|
||||
mv "$BIN_NAME" pzmanager
|
||||
chmod +x pzmanager
|
||||
fi
|
||||
|
||||
echo "==> Запускаю установщик"
|
||||
BINARY="$TMP/pzmanager" bash deploy/install.sh
|
||||
BINARY="$TMP/pzmanager" bash deploy/install.sh ${ARGS[@]+"${ARGS[@]}"}
|
||||
|
||||
+125
-9
@@ -1,10 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
# Установка и обновление pzmanager на Ubuntu/Debian. Запускать от root:
|
||||
# Установка, обновление и удаление pzmanager на Ubuntu/Debian. От root:
|
||||
#
|
||||
# sudo ./deploy/install.sh
|
||||
# sudo ./deploy/install.sh установить или обновить
|
||||
# sudo ./deploy/install.sh --uninstall убрать панель, данные оставить
|
||||
# sudo ./deploy/install.sh --uninstall --purge убрать вместе с мирами
|
||||
#
|
||||
# Скрипт идемпотентен: на уже настроенной машине он обновляет бинарник и
|
||||
# юнит, не трогая конфиг, миры и учётные записи панели.
|
||||
# Установка идемпотентна: на уже настроенной машине обновляются бинарник и
|
||||
# юнит, а конфиг, миры и учётные записи панели остаются нетронутыми.
|
||||
#
|
||||
# Переменные окружения:
|
||||
# PZ_HOME куда ставить всё хозяйство (по умолчанию /opt/pzmanager)
|
||||
@@ -29,11 +31,94 @@ ADMIN_SRC="${SCRIPT_DIR}/pzmanager-admin.sh"
|
||||
TARGET_BINARY=/usr/local/bin/pzmanager
|
||||
ADMIN_BINARY=/usr/local/bin/pzmanager-admin
|
||||
|
||||
UNINSTALL=0
|
||||
PURGE=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--uninstall | --remove) UNINSTALL=1 ;;
|
||||
--purge) UNINSTALL=1; PURGE=1 ;;
|
||||
--yes | -y) PZ_ASSUME_YES=1 ;;
|
||||
-h | --help)
|
||||
sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Неизвестный аргумент: $arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
echo "Запустите скрипт от root: sudo $0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# uninstall снимает панель с машины. Данные (миры, бэкапы, учётки) удаляются
|
||||
# только по --purge: потерять сохранения из-за неудачно набранной команды
|
||||
# гораздо обиднее, чем лишний раз убрать каталог руками.
|
||||
uninstall() {
|
||||
echo "==> Удаляю pzmanager"
|
||||
|
||||
if systemctl list-unit-files pzmanager.service &>/dev/null &&
|
||||
systemctl is-active --quiet pzmanager; then
|
||||
echo "==> Останавливаю службу (игровой сервер завершится штатно, это небыстро)"
|
||||
systemctl stop pzmanager || true
|
||||
fi
|
||||
systemctl disable --quiet pzmanager 2>/dev/null || true
|
||||
rm -f /etc/systemd/system/pzmanager.service
|
||||
systemctl daemon-reload
|
||||
systemctl reset-failed pzmanager 2>/dev/null || true
|
||||
|
||||
rm -f /usr/local/bin/pzmanager /usr/local/bin/pzmanager-admin /etc/sudoers.d/pzmanager
|
||||
echo "Служба, бинарники и правило sudo удалены."
|
||||
|
||||
if [[ "$PURGE" != "1" ]]; then
|
||||
echo
|
||||
echo "Данные остались в ${PZ_HOME} (миры, бэкапы, конфиг, учётные записи)."
|
||||
echo "Удалить и их: sudo $0 --uninstall --purge"
|
||||
return
|
||||
fi
|
||||
|
||||
# Защита от опечатки в PZ_HOME: сносим только то, что похоже на нашу
|
||||
# установку, и никогда — корень или домашние каталоги.
|
||||
local home_clean="${PZ_HOME%/}"
|
||||
if [[ -z "$home_clean" || "$home_clean" == "/" || "$home_clean" != /* ]]; then
|
||||
echo "!! PZ_HOME=${PZ_HOME} не похож на каталог установки — не трогаю" >&2
|
||||
return
|
||||
fi
|
||||
if [[ ! -e "$home_clean/config.yaml" && ! -d "$home_clean/data" ]]; then
|
||||
echo "!! В ${home_clean} нет ни config.yaml, ни data/ — не похоже на установку pzmanager," >&2
|
||||
echo " поэтому каталог оставлен нетронутым." >&2
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "${PZ_ASSUME_YES:-0}" != "1" ]]; then
|
||||
echo
|
||||
echo "Будет безвозвратно удалено: ${home_clean}"
|
||||
echo "Это миры, бэкапы, конфиг и учётные записи панели."
|
||||
if [[ -t 0 ]]; then
|
||||
read -r -p "Введите YES для подтверждения: " answer
|
||||
[[ "$answer" == "YES" ]] || { echo "Отменено."; return; }
|
||||
else
|
||||
echo "Запуск неинтерактивный — повторите с флагом --yes, если уверены." >&2
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -rf "$home_clean"
|
||||
if id "$PZ_USER" &>/dev/null; then
|
||||
deluser --system "$PZ_USER" &>/dev/null || userdel "$PZ_USER" &>/dev/null || true
|
||||
echo "Пользователь ${PZ_USER} удалён."
|
||||
fi
|
||||
echo "Удалено полностью: ${home_clean}"
|
||||
}
|
||||
|
||||
if [[ "$UNINSTALL" == "1" ]]; then
|
||||
uninstall
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ ! -f "$BINARY" ]]; then
|
||||
echo "Не найден бинарник $BINARY. Соберите его: make build-linux" >&2
|
||||
exit 1
|
||||
@@ -152,6 +237,14 @@ else
|
||||
echo "!! $ADMIN_SRC не найден или нет sudo — обновление из панели будет недоступно" >&2
|
||||
fi
|
||||
|
||||
# Кладём сам установщик рядом с данными: удалить или переустановить панель
|
||||
# можно будет одной локальной командой, без похода в репозиторий.
|
||||
install -m 0755 "$0" "$PZ_HOME/install.sh"
|
||||
if [[ -f "$ADMIN_SRC" ]]; then
|
||||
install -m 0755 "$ADMIN_SRC" "$PZ_HOME/pzmanager-admin.sh"
|
||||
fi
|
||||
install -m 0644 "$UNIT_SRC" "$PZ_HOME/pzmanager.service"
|
||||
|
||||
echo "==> Ставлю systemd-юнит"
|
||||
sed -e "s|/opt/pzmanager|${PZ_HOME}|g" \
|
||||
-e "s|User=pzserver|User=${PZ_USER}|" \
|
||||
@@ -178,8 +271,21 @@ if [[ "$MODE" == "update" ]]; then
|
||||
fi
|
||||
|
||||
systemctl start pzmanager
|
||||
# Дадим сервису секунду подняться, чтобы код настройки успел попасть в журнал.
|
||||
sleep 2
|
||||
|
||||
# Панель кладёт код первичной настройки в файл, как только поднимется. Ждём
|
||||
# его, чтобы показать код прямо здесь: иначе за ним пришлось бы идти в журнал.
|
||||
SETUP_TOKEN=""
|
||||
TOKEN_FILE="$PZ_HOME/data/setup-token"
|
||||
for _ in $(seq 1 20); do
|
||||
if [[ -s "$TOKEN_FILE" ]]; then
|
||||
SETUP_TOKEN="$(head -1 "$TOKEN_FILE")"
|
||||
break
|
||||
fi
|
||||
if ! systemctl is-active --quiet pzmanager; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Готово. Панель запущена и слушает ${PZ_LISTEN}."
|
||||
@@ -198,8 +304,18 @@ else
|
||||
echo " sudo ufw allow ${PORT}/tcp"
|
||||
echo
|
||||
fi
|
||||
echo "Код первичной настройки — в журнале:"
|
||||
echo
|
||||
echo " journalctl -u pzmanager -n 30 --no-pager"
|
||||
if [[ -n "$SETUP_TOKEN" ]]; then
|
||||
echo "Код первичной настройки (введите его в браузере):"
|
||||
echo
|
||||
echo " ${SETUP_TOKEN}"
|
||||
echo
|
||||
echo "Код одноразовый: после создания администратора он перестаёт работать."
|
||||
else
|
||||
echo "Код первичной настройки не удалось прочитать. Посмотрите журнал:"
|
||||
echo
|
||||
echo " journalctl -u pzmanager -n 30 --no-pager"
|
||||
fi
|
||||
echo
|
||||
echo "Игровые порты сервера: udp/16261 и udp/16262."
|
||||
echo
|
||||
echo "Удалить панель целиком: sudo ${PZ_HOME}/install.sh --uninstall --purge"
|
||||
|
||||
Reference in New Issue
Block a user