В обновлении видно изменения всех пропущенных версий
Панель показывала описание только последнего релиза. Пропустив пару версий, человек не узнал бы, что менялось в промежутке, — теперь запрашивается список релизов и показываются все, что новее установленной, каждая со своим заголовком и датой. Черновики и предрелизы отбрасываются, порядок восстанавливается по номерам версий, а не по тому, как их отдал Gitea. Заодно в карточке профиля появились подсказки: сколько на машине ОЗУ (не выбирать же лимит JVM вслепую) и что означает пустое поле начальной памяти. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+69
-14
@@ -16,6 +16,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -27,6 +28,14 @@ const adminCommand = "pzmanager-admin"
|
||||
// ErrNoAdmin возвращается, когда обновление из панели недоступно.
|
||||
var ErrNoAdmin = errors.New("обновление из панели недоступно: нет pzmanager-admin или правила sudo")
|
||||
|
||||
// ReleaseNote — одна версия в списке изменений.
|
||||
type ReleaseNote struct {
|
||||
Version string `json:"version"`
|
||||
Notes string `json:"notes"`
|
||||
Published int64 `json:"published"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// Info — то, что панель показывает в разделе обновления.
|
||||
type Info struct {
|
||||
Current string `json:"current"`
|
||||
@@ -40,6 +49,10 @@ type Info struct {
|
||||
CanApply bool `json:"can_apply"`
|
||||
// ManualCommand — что выполнить в терминале, если сама не может.
|
||||
ManualCommand string `json:"manual_command"`
|
||||
// Changelog — все версии между установленной и последней, свежие первыми.
|
||||
// Одного описания последнего релиза мало: пропустив пару версий, человек
|
||||
// не увидит, что менялось в промежутке.
|
||||
Changelog []ReleaseNote `json:"changelog"`
|
||||
// Error — почему не удалось узнать последнюю версию.
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
@@ -75,12 +88,14 @@ func (u *Updater) apiBase() (string, error) {
|
||||
return fmt.Sprintf("%s://%s/api/v1/repos/%s", scheme, host, path), nil
|
||||
}
|
||||
|
||||
// Check спрашивает у Gitea последний релиз и сравнивает его с текущей версией.
|
||||
// Check спрашивает у Gitea список релизов и оставляет те, что новее
|
||||
// установленной версии.
|
||||
func (u *Updater) Check(ctx context.Context) Info {
|
||||
info := Info{
|
||||
Current: u.Version,
|
||||
CanApply: u.CanApply(ctx),
|
||||
ManualCommand: u.manualCommand(),
|
||||
Changelog: []ReleaseNote{},
|
||||
}
|
||||
|
||||
api, err := u.apiBase()
|
||||
@@ -89,7 +104,10 @@ func (u *Updater) Check(ctx context.Context) Info {
|
||||
return info
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, api+"/releases/latest", nil)
|
||||
// limit с запасом: показываем изменения за все пропущенные версии, но не
|
||||
// тянем историю репозитория целиком.
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet,
|
||||
api+"/releases?limit=30&draft=false", nil)
|
||||
if err != nil {
|
||||
info.Error = err.Error()
|
||||
return info
|
||||
@@ -101,36 +119,73 @@ func (u *Updater) Check(ctx context.Context) Info {
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
info.Error = "в репозитории ещё нет релизов"
|
||||
return info
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
info.Error = "репозиторий ответил " + resp.Status
|
||||
return info
|
||||
}
|
||||
|
||||
var release struct {
|
||||
var releases []struct {
|
||||
TagName string `json:"tag_name"`
|
||||
Body string `json:"body"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
PublishedAt time.Time `json:"published_at"`
|
||||
Draft bool `json:"draft"`
|
||||
Prerelease bool `json:"prerelease"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
|
||||
if err := json.NewDecoder(resp.Body).Decode(&releases); err != nil {
|
||||
info.Error = "ответ репозитория не разобран: " + err.Error()
|
||||
return info
|
||||
}
|
||||
|
||||
info.Latest = release.TagName
|
||||
info.Notes = release.Body
|
||||
info.URL = release.HTMLURL
|
||||
if !release.PublishedAt.IsZero() {
|
||||
info.Published = release.PublishedAt.Unix()
|
||||
notes := make([]ReleaseNote, 0, len(releases))
|
||||
for _, r := range releases {
|
||||
if r.Draft || r.Prerelease || r.TagName == "" {
|
||||
continue
|
||||
}
|
||||
note := ReleaseNote{Version: r.TagName, Notes: r.Body, URL: r.HTMLURL}
|
||||
if !r.PublishedAt.IsZero() {
|
||||
note.Published = r.PublishedAt.Unix()
|
||||
}
|
||||
notes = append(notes, note)
|
||||
}
|
||||
info.Available = IsNewer(u.Version, release.TagName)
|
||||
if len(notes) == 0 {
|
||||
info.Error = "в репозитории ещё нет релизов"
|
||||
return info
|
||||
}
|
||||
|
||||
// Gitea отдаёт релизы свежими вперёд, но полагаться на это не станем.
|
||||
sort.Slice(notes, func(i, j int) bool {
|
||||
if IsNewer(notes[j].Version, notes[i].Version) {
|
||||
return true
|
||||
}
|
||||
if IsNewer(notes[i].Version, notes[j].Version) {
|
||||
return false
|
||||
}
|
||||
return notes[i].Published > notes[j].Published
|
||||
})
|
||||
|
||||
latest := notes[0]
|
||||
info.Latest = latest.Version
|
||||
info.Notes = latest.Notes
|
||||
info.URL = latest.URL
|
||||
info.Published = latest.Published
|
||||
info.Available = IsNewer(u.Version, latest.Version)
|
||||
info.Changelog = NewerThan(notes, u.Version)
|
||||
return info
|
||||
}
|
||||
|
||||
// NewerThan оставляет из списка релизы новее указанной версии, сохраняя
|
||||
// порядок «свежие первыми».
|
||||
func NewerThan(notes []ReleaseNote, current string) []ReleaseNote {
|
||||
out := make([]ReleaseNote, 0, len(notes))
|
||||
for _, note := range notes {
|
||||
if IsNewer(current, note.Version) {
|
||||
out = append(out, note)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// CanApply проверяет, что привилегированный скрипт на месте и sudo пускает к
|
||||
// нему без пароля. Проверяем вызовом: наличие файла ещё не значит, что
|
||||
// правило sudoers действует.
|
||||
|
||||
Reference in New Issue
Block a user