Workflow CI гоняет gofmt, vet, тесты и сборку на каждый push в main; workflow Release по тегу v* собирает бинарники под linux/amd64 и arm64, считает контрольные суммы и публикует релиз через API Gitea. Оба workflow забирают код через git, а не actions/checkout: это JS-действие, которому нужен Node внутри контейнера, а в образе golang его нет. Заодно сборка перестала зависеть от доступности github.com, что для self-hosted инстанса существенно. Скрипт deploy/get.sh ставит последний релиз одной командой: определяет архитектуру, забирает файлы релиза, сверяет sha256 (бинарник ставится с правами root) и передаёт управление install.sh. Обновление — та же команда. Версия сборки попадает в бинарник через ldflags, доступна как `pzmanager version` и видна в панели на вкладке «Настройки»: без неё нельзя понять, какой релиз стоит на сервере. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,50 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Собираем в официальном образе Go: версия компилятора фиксирована и
|
||||||
|
# совпадает с релизной сборкой.
|
||||||
|
container: golang:1.24
|
||||||
|
steps:
|
||||||
|
# Код забираем сами, а не через actions/checkout: это JS-действие, ему
|
||||||
|
# нужен Node внутри контейнера, которого в образе golang нет. Заодно
|
||||||
|
# сборка перестаёт зависеть от доступности github.com.
|
||||||
|
- name: Забрать код
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
git config --global --add safe.directory "$PWD"
|
||||||
|
git init -q .
|
||||||
|
git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||||
|
git -c http.extraheader="Authorization: token ${TOKEN}" \
|
||||||
|
fetch --depth 1 origin "${GITHUB_SHA}"
|
||||||
|
git checkout -q FETCH_HEAD
|
||||||
|
|
||||||
|
- name: Проверить форматирование
|
||||||
|
run: |
|
||||||
|
unformatted="$(gofmt -l cmd internal web)"
|
||||||
|
if [ -n "$unformatted" ]; then
|
||||||
|
echo "Не отформатировано (запустите gofmt -w):"
|
||||||
|
echo "$unformatted"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: go vet
|
||||||
|
run: go vet ./...
|
||||||
|
|
||||||
|
- name: Тесты
|
||||||
|
run: go test ./...
|
||||||
|
|
||||||
|
- name: Проверить сборку под Linux
|
||||||
|
run: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /dev/null ./cmd/pzmanager
|
||||||
|
|
||||||
|
- name: Проверить синтаксис скриптов установки
|
||||||
|
run: bash -n deploy/install.sh && bash -n deploy/get.sh
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: golang:1.24
|
||||||
|
steps:
|
||||||
|
# Код забираем сами: actions/checkout — JS-действие, которому нужен Node
|
||||||
|
# внутри контейнера, а в образе golang его нет. История нужна целиком,
|
||||||
|
# чтобы собрать список изменений с прошлого тега.
|
||||||
|
- name: Забрать код
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
git config --global --add safe.directory "$PWD"
|
||||||
|
git init -q .
|
||||||
|
git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||||
|
git -c http.extraheader="Authorization: token ${TOKEN}" \
|
||||||
|
fetch --tags origin
|
||||||
|
git checkout -q "${GITHUB_SHA}"
|
||||||
|
|
||||||
|
- name: Собрать бинарники
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
VERSION="${GITHUB_REF_NAME}"
|
||||||
|
mkdir -p dist
|
||||||
|
|
||||||
|
for arch in amd64 arm64; do
|
||||||
|
echo "==> pzmanager-linux-${arch}"
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH="${arch}" \
|
||||||
|
go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" \
|
||||||
|
-o "dist/pzmanager-linux-${arch}" ./cmd/pzmanager
|
||||||
|
done
|
||||||
|
|
||||||
|
# Скрипты установки кладём в релиз: тогда get.sh ставит версию
|
||||||
|
# целиком из одного источника и не зависит от состояния ветки.
|
||||||
|
cp deploy/install.sh deploy/pzmanager.service deploy/get.sh dist/
|
||||||
|
|
||||||
|
cd dist
|
||||||
|
sha256sum pzmanager-linux-* install.sh pzmanager.service get.sh > checksums.txt
|
||||||
|
cat checksums.txt
|
||||||
|
|
||||||
|
- name: Опубликовать релиз
|
||||||
|
env:
|
||||||
|
# Токен репозитория Actions выдаёт сам; RELEASE_TOKEN нужен, только
|
||||||
|
# если в настройках инстанса права такого токена урезаны.
|
||||||
|
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
DEFAULT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
apt-get update -qq && apt-get install -y -qq jq
|
||||||
|
|
||||||
|
TOKEN="${RELEASE_TOKEN:-$DEFAULT_TOKEN}"
|
||||||
|
if [ -z "$TOKEN" ]; then
|
||||||
|
echo "Нет токена для публикации: заведите секрет RELEASE_TOKEN" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
|
||||||
|
TAG="${GITHUB_REF_NAME}"
|
||||||
|
AUTH="Authorization: token ${TOKEN}"
|
||||||
|
|
||||||
|
# Список изменений с предыдущего тега; для первого релиза — вся
|
||||||
|
# история.
|
||||||
|
PREV="$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || true)"
|
||||||
|
if [ -n "$PREV" ]; then
|
||||||
|
CHANGES="$(git log --no-merges --pretty=format:'- %s' "${PREV}..${TAG}")"
|
||||||
|
else
|
||||||
|
CHANGES="$(git log --no-merges --pretty=format:'- %s')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
NOTES="$(printf '## Установка\n\n```bash\ncurl -fsSL %s/%s/raw/branch/main/deploy/get.sh | sudo bash\n```\n\nОбновление — та же команда: конфиг, миры и учётные записи не трогаются.\n\n## Изменения\n\n%s\n' \
|
||||||
|
"${GITHUB_SERVER_URL}" "${GITHUB_REPOSITORY}" "${CHANGES}")"
|
||||||
|
|
||||||
|
PAYLOAD="$(jq -n --arg tag "$TAG" --arg body "$NOTES" \
|
||||||
|
'{tag_name: $tag, name: $tag, body: $body, draft: false, prerelease: false}')"
|
||||||
|
|
||||||
|
# Тег могли перевыпустить — тогда работаем с существующим релизом.
|
||||||
|
ID="$(curl -sS -H "$AUTH" "${API}/releases/tags/${TAG}" | jq -r '.id // empty')"
|
||||||
|
|
||||||
|
if [ -n "$ID" ]; then
|
||||||
|
echo "==> Обновляю существующий релиз ${TAG} (id ${ID})"
|
||||||
|
curl -sS -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
|
||||||
|
-d "$PAYLOAD" "${API}/releases/${ID}" > /dev/null
|
||||||
|
# Старые файлы удаляем: иначе загрузка упрётся в совпадающие имена.
|
||||||
|
curl -sS -H "$AUTH" "${API}/releases/${ID}/assets" | jq -r '.[].id' \
|
||||||
|
| while read -r asset; do
|
||||||
|
[ -n "$asset" ] || continue
|
||||||
|
curl -sS -X DELETE -H "$AUTH" "${API}/releases/${ID}/assets/${asset}" > /dev/null
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "==> Создаю релиз ${TAG}"
|
||||||
|
ID="$(curl -sS -X POST -H "$AUTH" -H "Content-Type: application/json" \
|
||||||
|
-d "$PAYLOAD" "${API}/releases" | jq -r '.id')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$ID" ] || [ "$ID" = "null" ]; then
|
||||||
|
echo "Не удалось получить id релиза — проверьте права токена" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for file in dist/*; do
|
||||||
|
name="$(basename "$file")"
|
||||||
|
echo "==> Загружаю ${name}"
|
||||||
|
curl -sS -f -X POST -H "$AUTH" \
|
||||||
|
-F "attachment=@${file}" \
|
||||||
|
"${API}/releases/${ID}/assets?name=${name}" > /dev/null
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Готово: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/tag/${TAG}"
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
BINARY := pzmanager
|
BINARY := pzmanager
|
||||||
LDFLAGS := -s -w
|
# Версия попадает в бинарник и видна в панели и в `pzmanager version`.
|
||||||
|
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
|
||||||
|
LDFLAGS := -s -w -X main.version=$(VERSION)
|
||||||
|
|
||||||
.PHONY: build build-linux test vet fmt run clean
|
.PHONY: build build-linux test vet fmt run clean
|
||||||
|
|
||||||
|
|||||||
@@ -47,21 +47,30 @@ systemd ──> pzmanager ──> start-server.sh ──> java (GameServer)
|
|||||||
|
|
||||||
## Быстрый старт
|
## Быстрый старт
|
||||||
|
|
||||||
Собрать бинарник (нужен Go 1.24+; собирать можно на любой ОС, включая Windows):
|
На чистом Ubuntu/Debian всё ставится одной командой — она возьмёт последний
|
||||||
|
релиз, проверит контрольные суммы и настроит systemd:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make build-linux
|
curl -fsSL https://gitea.hsrv.site/mrleo1nid/pzmanager/raw/branch/main/deploy/get.sh | sudo bash
|
||||||
```
|
```
|
||||||
|
|
||||||
Скопировать `pzmanager` и папку `deploy/` на сервер и запустить установщик:
|
По умолчанию панель слушает `127.0.0.1:8080` — только с самой машины. Чтобы
|
||||||
|
она была доступна в локальной сети, укажите адрес сервера:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo ./deploy/install.sh
|
curl -fsSL https://gitea.hsrv.site/mrleo1nid/pzmanager/raw/branch/main/deploy/get.sh \
|
||||||
|
| sudo PZ_LISTEN=10.10.1.142:8080 bash
|
||||||
```
|
```
|
||||||
|
|
||||||
Установщик заведёт системного пользователя `pzserver`, поставит зависимости
|
Переменные: `PZ_LISTEN` (адрес панели), `PZ_HOME` (каталог, по умолчанию
|
||||||
(SteamCMD, JRE, SDL2), положит бинарник в `/usr/local/bin`, создаст структуру
|
`/opt/pzmanager`), `PZ_USER` (системный пользователь), `PZ_VERSION`
|
||||||
в `/opt/pzmanager` и включит systemd-юнит:
|
(конкретный тег вместо последнего).
|
||||||
|
|
||||||
|
Скрипт запускается от root, так что перед первым запуском его разумно
|
||||||
|
прочитать: [deploy/get.sh](deploy/get.sh).
|
||||||
|
|
||||||
|
Установщик поставит зависимости (SteamCMD, JRE, SDL2), заведёт пользователя
|
||||||
|
`pzserver` без shell, положит бинарник в `/usr/local/bin` и создаст структуру:
|
||||||
|
|
||||||
```
|
```
|
||||||
/opt/pzmanager/
|
/opt/pzmanager/
|
||||||
@@ -72,17 +81,8 @@ sudo ./deploy/install.sh
|
|||||||
└── backups/ архивы
|
└── backups/ архивы
|
||||||
```
|
```
|
||||||
|
|
||||||
Куда ставить и на каком адресе слушать, задаётся переменными окружения:
|
**Обновление** — та же команда: конфиг, миры и учётные записи не трогаются,
|
||||||
|
меняется только бинарник.
|
||||||
```bash
|
|
||||||
# панель доступна в локальной сети по адресу машины
|
|
||||||
sudo PZ_LISTEN=10.10.1.142:8080 ./deploy/install.sh
|
|
||||||
|
|
||||||
# другой каталог и пользователь
|
|
||||||
sudo PZ_HOME=/srv/pz PZ_USER=zomboid ./deploy/install.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
По умолчанию панель слушает `127.0.0.1:8080` — только с самой машины.
|
|
||||||
|
|
||||||
Дальше:
|
Дальше:
|
||||||
|
|
||||||
@@ -113,8 +113,20 @@ sudo ufw allow 16261/udp # игровой порт
|
|||||||
sudo ufw allow 16262/udp # второй игровой порт
|
sudo ufw allow 16262/udp # второй игровой порт
|
||||||
```
|
```
|
||||||
|
|
||||||
Обновление ставится поверх: соберите новый бинарник и повторите
|
### Установка из исходников
|
||||||
`sudo ./deploy/install.sh` — конфиг, миры и учётки останутся на месте.
|
|
||||||
|
Если релизам предпочитаете свою сборку (нужен Go 1.24+, собирать можно на
|
||||||
|
любой ОС, включая Windows):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make build-linux
|
||||||
|
```
|
||||||
|
|
||||||
|
Скопируйте `pzmanager` и папку `deploy/` на сервер и запустите установщик:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo PZ_LISTEN=10.10.1.142:8080 ./deploy/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
## Моды
|
## Моды
|
||||||
|
|
||||||
@@ -290,6 +302,33 @@ backup:
|
|||||||
Тем не менее доступ к панели равносилен доступу к игровому серверу, так что
|
Тем не менее доступ к панели равносилен доступу к игровому серверу, так что
|
||||||
пароль стоит выбрать не короче того, что вы поставили бы на SSH.
|
пароль стоит выбрать не короче того, что вы поставили бы на SSH.
|
||||||
|
|
||||||
|
## Сборка и релизы
|
||||||
|
|
||||||
|
Репозиторий собирается Gitea Actions ([`.gitea/workflows`](.gitea/workflows)):
|
||||||
|
|
||||||
|
- **CI** — на каждый push и pull request в `main`: `gofmt`, `go vet`, тесты,
|
||||||
|
проверка сборки под Linux и синтаксиса скриптов установки.
|
||||||
|
- **Release** — на тег `v*`: собирает бинарники под `linux/amd64` и
|
||||||
|
`linux/arm64`, считает контрольные суммы и публикует релиз с ними и
|
||||||
|
скриптами установки.
|
||||||
|
|
||||||
|
Выпуск новой версии:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git tag -a v1.2.3 -m "Описание релиза" && git push origin v1.2.3
|
||||||
|
```
|
||||||
|
|
||||||
|
Версия попадает в бинарник через `-ldflags -X main.version=` и видна в
|
||||||
|
`pzmanager version` и в панели на вкладке «Настройки».
|
||||||
|
|
||||||
|
Workflow не зависят от github.com и от Node внутри контейнера: код забирается
|
||||||
|
через `git`, а релиз публикуется прямо в API Gitea. Токен Actions выдаёт сам;
|
||||||
|
если в настройках инстанса его права урезаны, заведите секрет `RELEASE_TOKEN`
|
||||||
|
с доступом на запись в репозиторий.
|
||||||
|
|
||||||
|
Для работы Actions нужен зарегистрированный runner с меткой `ubuntu-latest`,
|
||||||
|
умеющий запускать контейнеры (`docker`).
|
||||||
|
|
||||||
## Разработка
|
## Разработка
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
+13
-1
@@ -29,6 +29,10 @@ import (
|
|||||||
"github.com/mrleo1nid/pzmanager/web"
|
"github.com/mrleo1nid/pzmanager/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// version подставляется при сборке: -ldflags "-X main.version=v1.2.3".
|
||||||
|
// В сборке из рабочей копии остаётся "dev".
|
||||||
|
var version = "dev"
|
||||||
|
|
||||||
const usage = `pzmanager — панель управления сервером Project Zomboid.
|
const usage = `pzmanager — панель управления сервером Project Zomboid.
|
||||||
|
|
||||||
Использование:
|
Использование:
|
||||||
@@ -36,6 +40,7 @@ const usage = `pzmanager — панель управления сервером
|
|||||||
pzmanager useradd <логин> создать пользователя панели
|
pzmanager useradd <логин> создать пользователя панели
|
||||||
pzmanager passwd <логин> сменить пароль
|
pzmanager passwd <логин> сменить пароль
|
||||||
pzmanager users показать список пользователей
|
pzmanager users показать список пользователей
|
||||||
|
pzmanager version показать версию
|
||||||
|
|
||||||
Флаги:
|
Флаги:
|
||||||
`
|
`
|
||||||
@@ -56,6 +61,12 @@ func main() {
|
|||||||
if len(args) > 0 && !strings.HasPrefix(args[0], "-") {
|
if len(args) > 0 && !strings.HasPrefix(args[0], "-") {
|
||||||
command, args = args[0], args[1:]
|
command, args = args[0], args[1:]
|
||||||
}
|
}
|
||||||
|
// Версию печатаем до загрузки конфига: она должна работать всегда,
|
||||||
|
// в том числе на сломанной конфигурации.
|
||||||
|
if command == "version" || command == "--version" {
|
||||||
|
fmt.Println("pzmanager", version)
|
||||||
|
return
|
||||||
|
}
|
||||||
if err := fs.Parse(args); err != nil {
|
if err := fs.Parse(args); err != nil {
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
@@ -123,6 +134,7 @@ func serve(cfg *config.Config, users *auth.Store, secureCookies bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
apiServer := api.New(api.Options{
|
apiServer := api.New(api.Options{
|
||||||
|
Version: version,
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
Users: users,
|
Users: users,
|
||||||
Supervisor: supervisor,
|
Supervisor: supervisor,
|
||||||
@@ -155,7 +167,7 @@ func serve(cfg *config.Config, users *auth.Store, secureCookies bool) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("не удалось занять адрес %s: %w", cfg.Listen, err)
|
return fmt.Errorf("не удалось занять адрес %s: %w", cfg.Listen, err)
|
||||||
}
|
}
|
||||||
fmt.Printf("pzmanager слушает http://%s\n", cfg.Listen)
|
fmt.Printf("pzmanager %s слушает http://%s\n", version, cfg.Listen)
|
||||||
|
|
||||||
if cfg.AutoStart {
|
if cfg.AutoStart {
|
||||||
if err := supervisor.Start(); err != nil {
|
if err := supervisor.Start(); err != nil {
|
||||||
|
|||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
#!/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
|
||||||
|
[[ "$required" == "optional" ]] && return 1
|
||||||
|
die "в релизе ${TAG} нет файла ${name}"
|
||||||
|
fi
|
||||||
|
curl -fsSL -o "$name" "$url" || die "не удалось скачать ${name}"
|
||||||
|
}
|
||||||
|
|
||||||
|
download "$BIN_NAME"
|
||||||
|
download install.sh
|
||||||
|
download pzmanager.service
|
||||||
|
|
||||||
|
# Контрольные суммы обязательны: бинарник ставится с правами root.
|
||||||
|
download checksums.txt
|
||||||
|
echo "==> Проверяю контрольные суммы"
|
||||||
|
grep -E " (${BIN_NAME}|install\.sh|pzmanager\.service)\$" checksums.txt > expected.txt \
|
||||||
|
|| die "в checksums.txt нет нужных строк"
|
||||||
|
sha256sum -c expected.txt || die "контрольные суммы не сошлись — файлы повреждены"
|
||||||
|
|
||||||
|
mkdir -p deploy
|
||||||
|
mv install.sh pzmanager.service deploy/
|
||||||
|
mv "$BIN_NAME" pzmanager
|
||||||
|
chmod +x pzmanager deploy/install.sh
|
||||||
|
|
||||||
|
echo "==> Запускаю установщик"
|
||||||
|
BINARY="$TMP/pzmanager" bash deploy/install.sh
|
||||||
@@ -127,6 +127,7 @@ type managerConfigDTO struct {
|
|||||||
BackupKeep int `json:"backup_keep"`
|
BackupKeep int `json:"backup_keep"`
|
||||||
BackupStopServer bool `json:"backup_stop_server"`
|
BackupStopServer bool `json:"backup_stop_server"`
|
||||||
ConfigPath string `json:"config_path"`
|
ConfigPath string `json:"config_path"`
|
||||||
|
Version string `json:"version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleGetManagerConfig(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) handleGetManagerConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -145,6 +146,7 @@ func (s *Server) handleGetManagerConfig(w http.ResponseWriter, r *http.Request)
|
|||||||
BackupKeep: c.Backup.Keep,
|
BackupKeep: c.Backup.Keep,
|
||||||
BackupStopServer: c.Backup.StopServer,
|
BackupStopServer: c.Backup.StopServer,
|
||||||
ConfigPath: c.Path(),
|
ConfigPath: c.Path(),
|
||||||
|
Version: s.version,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ const csrfHeader = "X-PZM-Request"
|
|||||||
|
|
||||||
// Server связывает HTTP-слой со всеми подсистемами менеджера.
|
// Server связывает HTTP-слой со всеми подсистемами менеджера.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
|
version string
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
users *auth.Store
|
users *auth.Store
|
||||||
sup *pzserver.Supervisor
|
sup *pzserver.Supervisor
|
||||||
@@ -43,6 +44,8 @@ type Server struct {
|
|||||||
|
|
||||||
// Options — то, что нужно собрать серверу API.
|
// Options — то, что нужно собрать серверу API.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
// Version — версия сборки, панель показывает её в настройках.
|
||||||
|
Version string
|
||||||
Config *config.Config
|
Config *config.Config
|
||||||
Users *auth.Store
|
Users *auth.Store
|
||||||
Supervisor *pzserver.Supervisor
|
Supervisor *pzserver.Supervisor
|
||||||
@@ -57,6 +60,7 @@ type Options struct {
|
|||||||
// New собирает HTTP-обработчик панели.
|
// New собирает HTTP-обработчик панели.
|
||||||
func New(o Options) *Server {
|
func New(o Options) *Server {
|
||||||
return &Server{
|
return &Server{
|
||||||
|
version: o.Version,
|
||||||
cfg: o.Config,
|
cfg: o.Config,
|
||||||
users: o.Users,
|
users: o.Users,
|
||||||
sup: o.Supervisor,
|
sup: o.Supervisor,
|
||||||
|
|||||||
+2
-1
@@ -1198,7 +1198,8 @@ async function loadSettings() {
|
|||||||
|
|
||||||
const pathNote = document.createElement("p");
|
const pathNote = document.createElement("p");
|
||||||
pathNote.className = "muted small";
|
pathNote.className = "muted small";
|
||||||
pathNote.textContent = `Файл конфигурации: ${config.config_path}`;
|
pathNote.textContent =
|
||||||
|
`PZ Manager ${config.version || "?"} · файл конфигурации: ${config.config_path}`;
|
||||||
form.appendChild(pathNote);
|
form.appendChild(pathNote);
|
||||||
|
|
||||||
await loadUsers();
|
await loadUsers();
|
||||||
|
|||||||
Reference in New Issue
Block a user