Установщик стал идемпотентным: на уже настроенной машине он определяет установленную версию, останавливает службу, меняет бинарник и поднимает её обратно, не трогая конфиг, миры и учётные записи. Зависимости при обновлении не переустанавливаются — это лишние минуты apt. Кнопки «Обновить» и «Перезапустить панель» появились на вкладке «Настройки» вместе с текущей версией и описанием нового релиза. Панель работает от непривилегированного пользователя и не может ни заменить свой бинарник в /usr/local/bin, ни дёрнуть systemctl. Вместо того чтобы расширять её права, установщик кладёт root-скрипт pzmanager-admin и разрешает сервисному пользователю через sudoers ровно эту одну команду. Скрипт сверяет sha256 скачанного бинарника с checksums.txt релиза. Если правила sudo нет, кнопки не будет — панель покажет команду для терминала. Перед обновлением и перезапуском игровой сервер останавливается штатным quit: иначе его погасил бы systemd уже в момент рестарта, и ожидание растянулось бы на таймаут остановки. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
60 lines
2.0 KiB
Go
60 lines
2.0 KiB
Go
package updater
|
|
|
|
import "testing"
|
|
|
|
func TestIsNewer(t *testing.T) {
|
|
cases := []struct {
|
|
current, latest string
|
|
want bool
|
|
}{
|
|
{"v1.0.0", "v1.0.1", true},
|
|
{"v1.0.0", "v1.1.0", true},
|
|
{"v1.0.0", "v2.0.0", true},
|
|
{"v1.2.3", "v1.2.3", false},
|
|
{"v1.2.3", "v1.2.2", false},
|
|
{"v2.0.0", "v1.9.9", false},
|
|
// Сборка из рабочей копии не знает, что в ней, — предлагаем релиз.
|
|
{"dev", "v1.0.0", true},
|
|
{"", "v1.0.0", true},
|
|
// Без релизов обновляться некуда.
|
|
{"v1.0.0", "", false},
|
|
// Разное число компонентов.
|
|
{"v1.0", "v1.0.1", true},
|
|
{"1.0.0", "v1.0.0", false},
|
|
// Предрелизные суффиксы в сравнении чисел не участвуют.
|
|
{"v1.0.0-rc1", "v1.0.0", false},
|
|
{"v1.0.0", "v1.0.1-rc1", true},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
if got := IsNewer(c.current, c.latest); got != c.want {
|
|
t.Errorf("IsNewer(%q, %q) = %v, ожидалось %v", c.current, c.latest, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAPIBase(t *testing.T) {
|
|
cases := map[string]string{
|
|
"https://gitea.hsrv.site/mrleo1nid/pzmanager": "https://gitea.hsrv.site/api/v1/repos/mrleo1nid/pzmanager",
|
|
"https://gitea.hsrv.site/mrleo1nid/pzmanager.git": "https://gitea.hsrv.site/api/v1/repos/mrleo1nid/pzmanager",
|
|
"https://gitea.hsrv.site/mrleo1nid/pzmanager/": "https://gitea.hsrv.site/api/v1/repos/mrleo1nid/pzmanager",
|
|
"http://192.168.0.5:3000/team/pzmanager": "http://192.168.0.5:3000/api/v1/repos/team/pzmanager",
|
|
}
|
|
for repo, want := range cases {
|
|
got, err := New(repo, "dev").apiBase()
|
|
if err != nil {
|
|
t.Errorf("apiBase(%q): %v", repo, err)
|
|
continue
|
|
}
|
|
if got != want {
|
|
t.Errorf("apiBase(%q) = %q, ожидалось %q", repo, got, want)
|
|
}
|
|
}
|
|
|
|
for _, bad := range []string{"gitea.hsrv.site/owner/repo", "https://gitea.hsrv.site", ""} {
|
|
if _, err := New(bad, "dev").apiBase(); err == nil {
|
|
t.Errorf("адрес %q принят, ожидалась ошибка", bad)
|
|
}
|
|
}
|
|
}
|