feat(update): отдельный update.base_url и вшитый токен read:package
All checks were successful
CI / test (push) Successful in 43s
CI / build-and-package (amd64, darwin) (push) Successful in 40s
CI / build-and-package (amd64, linux) (push) Successful in 39s
CI / build-and-package (amd64, windows) (push) Successful in 39s
CI / build-and-package (arm64, darwin) (push) Successful in 36s
CI / build-and-package (arm64, linux) (push) Successful in 39s
All checks were successful
CI / test (push) Successful in 43s
CI / build-and-package (amd64, darwin) (push) Successful in 40s
CI / build-and-package (amd64, linux) (push) Successful in 39s
CI / build-and-package (amd64, windows) (push) Successful in 39s
CI / build-and-package (arm64, darwin) (push) Successful in 36s
CI / build-and-package (arm64, linux) (push) Successful in 39s
- update.base_url (env UPDATE_BASE_URL) — хост обновления, БЕЗ фоллбэка на git.base_url: пустое значение отключает update. - Убран git.token из пути обновления: теперь отдельный токен. - Токен read:package вшивается при сборке (-X main.updateToken из секрета TC_UPDATE_TOKEN), приоритет над update.token из конфига. - Обновлены app.New (сигнатура), main, Makefile, CI, config.example, тесты.
This commit is contained in:
@@ -58,7 +58,7 @@ jobs:
|
||||
fi
|
||||
VERSION="commit-$(echo '${{ gitea.sha }}' | cut -c1-7)"
|
||||
GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} \
|
||||
go build -ldflags="-s -w -X main.version=${VERSION}" -o "${FILENAME}" ./cmd/ratatoskr/
|
||||
go build -ldflags="-s -w -X main.version=${VERSION} -X main.updateToken=${{ secrets.TC_UPDATE_TOKEN }}" -o "${FILENAME}" ./cmd/ratatoskr/
|
||||
echo "FILENAME=${FILENAME}" >> "${GITEA_ENV}"
|
||||
echo "VERSION=${VERSION}" >> "${GITEA_ENV}"
|
||||
|
||||
|
||||
3
Makefile
3
Makefile
@@ -5,7 +5,8 @@ CMD_DIR := ./cmd/ratatoskr
|
||||
GO ?= go
|
||||
GOPATH := $(shell $(GO) env GOPATH)
|
||||
GIT_SHA := $(shell git rev-parse --short HEAD 2>/dev/null || echo dev)
|
||||
GOFLAGS ?= -ldflags="-s -w -X main.version=commit-$(GIT_SHA)"
|
||||
UPDATE_TOKEN ?= ""
|
||||
GOFLAGS ?= -ldflags="-s -w -X main.version=commit-$(GIT_SHA) -X main.updateToken=$(UPDATE_TOKEN)"
|
||||
|
||||
all: test build
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@ import (
|
||||
// Локальная сборка без ldflags оставляет "dev".
|
||||
var version = "dev"
|
||||
|
||||
// updateToken — токен read:package для авто-обновления, вшивается при сборке:
|
||||
// go build -ldflags "-X main.updateToken=<token>"
|
||||
// Без ldflags — пустой; тогда используется update.token из конфига (если задан).
|
||||
var updateToken = ""
|
||||
|
||||
func main() {
|
||||
cfg := flag.String("config", "", "путь к config.yaml (по умолчанию — CWD/config.yaml)")
|
||||
versionFlag := flag.Bool("version", false, "показать версию и выйти")
|
||||
@@ -29,7 +34,7 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
a, err := app.New(*cfg, version)
|
||||
a, err := app.New(*cfg, version, updateToken)
|
||||
if err != nil {
|
||||
log.Fatalf("app init: %v", err)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,10 @@ telegram:
|
||||
|
||||
# update (авто-обновление бинаря из Gitea Packages)
|
||||
# Авто-режим только проверяет и уведомляет владельца; замена — по команде /update.
|
||||
# Если base_url не задан — обновление отключено (фоллбэк на git.base_url НЕ используется).
|
||||
# enabled: false # включить фоновую проверку обновления
|
||||
# base_url: "http://gitea.hal9000.home" # хост Gitea для обновления (UPDATE_BASE_URL)
|
||||
# token: "${UPDATE_TOKEN}" # токен read:package (UPDATE_TOKEN) — опционально, если не вшит
|
||||
# owner: "kamelion" # владелец пакета в Gitea (UPDATE_OWNER)
|
||||
# package: "ratatoskr" # имя пакета-хранилища (generic)
|
||||
# check_interval: "24h" # как часто проверять наличие обновления
|
||||
|
||||
@@ -38,7 +38,10 @@ type App struct {
|
||||
|
||||
// New читает конфиг и собирает все зависимости.
|
||||
// Не запускает подсистемы (Run).
|
||||
func New(configPath, version string) (*App, error) {
|
||||
// version — вшитая версия бинаря (ldflag -X main.version).
|
||||
// updateToken — вшитый токен read:package для авто-обновления
|
||||
// (ldflag -X main.updateToken); имеет приоритет над update.token из конфига.
|
||||
func New(configPath, version, updateToken string) (*App, error) {
|
||||
cfg, err := config.Load(configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrConfig, err)
|
||||
@@ -113,12 +116,17 @@ func New(configPath, version string) (*App, error) {
|
||||
a.Router = router
|
||||
a.Worker = w
|
||||
|
||||
// Auto-обновление: .new/.old рядом с бинарником (Dir пуст → binDir() от os.Executable)
|
||||
// Auto-обновление: .new/.old рядом с бинарником (Dir пуст → binDir() от os.Executable).
|
||||
// Токен: вшитый updateToken приоритетнее update.token из конфига.
|
||||
udToken := updateToken
|
||||
if udToken == "" {
|
||||
udToken = cfg.Update.Token
|
||||
}
|
||||
ud := &update.Updater{
|
||||
BaseURL: cfg.Git.BaseURL,
|
||||
BaseURL: cfg.Update.BaseURL,
|
||||
Owner: cfg.Update.Owner,
|
||||
Package: cfg.Update.Package,
|
||||
Token: cfg.Git.Token,
|
||||
Token: udToken,
|
||||
CurrentVersion: version,
|
||||
}
|
||||
a.Updater = ud
|
||||
@@ -369,7 +377,7 @@ func (a *App) version() string {
|
||||
// При активной задаче в чате — требует подтверждения, чтобы не прерывать работу.
|
||||
func (a *App) handleUpdateCmd(ctx context.Context, uid chat.UserID) {
|
||||
if a.Updater == nil || a.Updater.BaseURL == "" || a.Updater.Owner == "" {
|
||||
a.send(ctx, uid, "Обновление не настроено (нужен git.base_url и update.owner в config).")
|
||||
a.send(ctx, uid, "Обновление не настроено (нужен update.base_url и update.owner в config).")
|
||||
return
|
||||
}
|
||||
if !a.Config.Update.Enabled {
|
||||
|
||||
@@ -20,7 +20,7 @@ func TestNew(t *testing.T) {
|
||||
t.Setenv("RATATOSKR_DB", dbPath)
|
||||
|
||||
// Загружаем без config-файла (дефолты + env)
|
||||
a, err := New("", "dev")
|
||||
a, err := New("", "dev", "")
|
||||
if err != nil {
|
||||
t.Fatalf("New() err = %v", err)
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func TestNew_MissingToken(t *testing.T) {
|
||||
t.Setenv("TG_CHAT_ID", "12345")
|
||||
t.Setenv("RATATOSKR_DB", dbPath)
|
||||
|
||||
_, err := New("", "dev")
|
||||
_, err := New("", "dev", "")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for missing token")
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func TestNew_BadDB(t *testing.T) {
|
||||
t.Setenv("TG_CHAT_ID", "12345")
|
||||
t.Setenv("RATATOSKR_DB", dbPath)
|
||||
|
||||
_, err := New("", "dev")
|
||||
_, err := New("", "dev", "")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid db path")
|
||||
}
|
||||
@@ -82,7 +82,7 @@ func TestNew_RunCtxCancel(t *testing.T) {
|
||||
t.Setenv("TG_CHAT_ID", "12345")
|
||||
t.Setenv("RATATOSKR_DB", dbPath)
|
||||
|
||||
a, err := New("", "dev")
|
||||
a, err := New("", "dev", "")
|
||||
if err != nil {
|
||||
t.Fatalf("New() err = %v", err)
|
||||
}
|
||||
@@ -106,4 +106,57 @@ func TestMain(t *testing.T) {
|
||||
t.Fatalf("main.go not found: %v", err)
|
||||
}
|
||||
t.Log("main.go exists")
|
||||
}
|
||||
|
||||
// TestNew_UpdateWiring проверяет: 1) update.base_url берётся из update-блока
|
||||
// (не git.base_url); 2) приоритет токена: вшитый updateToken > update.token.
|
||||
func TestNew_UpdateWiring(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
dbPath := filepath.Join(tmp, "test.db")
|
||||
|
||||
configPath := filepath.Join(tmp, "config.yaml")
|
||||
content := strings.Join([]string{
|
||||
"telegram:",
|
||||
" token: \"test:token\"",
|
||||
" chat_id: \"12345\"",
|
||||
"git:",
|
||||
" base_url: \"http://local-git/\"",
|
||||
" token: \"git-token\"",
|
||||
"update:",
|
||||
" enabled: true",
|
||||
" base_url: \"https://hub.example.com\"",
|
||||
" token: \"cfg-update-token\"",
|
||||
" owner: \"kamelion\"",
|
||||
"paths:",
|
||||
" db: \"" + dbPath + "\"",
|
||||
"", // пустая строка в конце
|
||||
}, "\n")
|
||||
if err := os.WriteFile(configPath, []byte(content), 0o600); err != nil {
|
||||
t.Fatalf("write config: %v", err)
|
||||
}
|
||||
|
||||
// 1) base_url из update-блока, а НЕ git
|
||||
a, err := New(configPath, "dev", "")
|
||||
if err != nil {
|
||||
t.Fatalf("New() err = %v", err)
|
||||
}
|
||||
if a.Updater == nil {
|
||||
t.Fatal("Updater не создан")
|
||||
}
|
||||
if want := "https://hub.example.com"; a.Updater.BaseURL != want {
|
||||
t.Errorf("BaseURL = %q, want %q (должен быть update.base_url, не git.base_url)", a.Updater.BaseURL, want)
|
||||
}
|
||||
// без вшитого токена берём update.token из конфига
|
||||
if a.Updater.Token != "cfg-update-token" {
|
||||
t.Errorf("Token = %q, want cfg-update-token (при пустом вшитом)", a.Updater.Token)
|
||||
}
|
||||
|
||||
// 2) вшитый updateToken перекрывает оба источника
|
||||
a2, err := New(configPath, "dev", "embedded-update-token")
|
||||
if err != nil {
|
||||
t.Fatalf("New() err = %v", err)
|
||||
}
|
||||
if a2.Updater.Token != "embedded-update-token" {
|
||||
t.Errorf("Token = %q, want embedded-update-token (вшитый приоритетнее)", a2.Updater.Token)
|
||||
}
|
||||
}
|
||||
@@ -50,12 +50,17 @@ type GitCfg struct {
|
||||
|
||||
// UpdateCfg — автообновление.
|
||||
// Enabled=false отключает и авто-проверку, и /update.
|
||||
// BaseURL — хост Gitea для обновления (обязателен: без него update отключён,
|
||||
// фоллбэк на git.base_url НЕ используется).
|
||||
// Token — токен read:package. Приоритет: вшитый -X main.updateToken > этот.
|
||||
// Owner — владелец Gitea-пакета (обязателен для работы обновления).
|
||||
// Package — имя пакета-хранилища (по умолчанию ratatoskr).
|
||||
// CheckInterval — как часто авто-проверять наличие обновления
|
||||
// (по умолчанию 24h). Авто-режим ТОЛЬКО уведомляет; замена — по /update.
|
||||
type UpdateCfg struct {
|
||||
Enabled bool `yaml:"enabled" default:"false"`
|
||||
BaseURL string `yaml:"base_url" env:"UPDATE_BASE_URL"`
|
||||
Token string `yaml:"token" env:"UPDATE_TOKEN"`
|
||||
Owner string `yaml:"owner" env:"UPDATE_OWNER"`
|
||||
Package string `yaml:"package" default:"ratatoskr"`
|
||||
CheckInterval Duration `yaml:"check_interval" default:"24h"`
|
||||
|
||||
Reference in New Issue
Block a user