Files
pzmanager/deploy/get.sh
T
mrleo1nidandClaude Opus 5 e25294db16
CI / test (push) Successful in 21s
Release / release (push) Successful in 29s
Код настройки печатает установщик, появилось удаление панели
Менеджер кладёт код первичной настройки в data/setup-token, установщик
дожидается файла и печатает код последним сообщением — за ним больше не
нужно идти в journalctl. Файл удаляется, как только создан администратор,
и не появляется на уже настроенной панели.

Установщик научился снимать панель с машины: --uninstall убирает службу,
бинарники и правило sudo, --purge вдобавок сносит каталог с мирами,
бэкапами и учётками. Данные удаляются только по явному подтверждению, а
каталог сносится, лишь если похож на установку панели: опечатка в PZ_HOME
не должна стоить чужих файлов. Копия установщика теперь лежит в
/opt/pzmanager, так что удалять можно без похода в репозиторий.

get.sh пробрасывает аргументы установщику и в режиме удаления не качает
бинарник. Заодно исправлена подстановка пустого массива аргументов:
"${ARGS[@]:-}" разворачивается в один пустой аргумент, из-за чего обычная
установка спотыкалась бы о него.

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

160 lines
6.4 KiB
Bash
Raw 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
#
# Удаление (аргументы уходят установщику как есть):
#
# 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)
# 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)"
# Аргументы уходят установщику. Для удаления бинарник качать незачем — это
# лишние восемь мегабайт ради скрипта, который его же и сотрёт.
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
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}"
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
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 install.sh
download pzmanager.service
FILES=(install.sh pzmanager.service)
if [[ "$NEED_BINARY" == "1" ]]; then
download "$BIN_NAME"
FILES+=("$BIN_NAME")
fi
# Скрипт привилегированных действий появился не сразу: со старым релизом
# ставим без него, панель просто не покажет кнопку обновления.
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
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 ${ARGS[@]+"${ARGS[@]}"}