fix(update): убрать псевдо-версию latest, всё в commit-<sha7>
All checks were successful
CI / test (push) Successful in 59s
CI / build-and-package (amd64, darwin) (push) Successful in 42s
CI / build-and-package (amd64, linux) (push) Successful in 40s
CI / build-and-package (amd64, windows) (push) Successful in 42s
CI / build-and-package (arm64, darwin) (push) Successful in 45s
CI / build-and-package (arm64, linux) (push) Successful in 41s
All checks were successful
CI / test (push) Successful in 59s
CI / build-and-package (amd64, darwin) (push) Successful in 42s
CI / build-and-package (amd64, linux) (push) Successful in 40s
CI / build-and-package (amd64, windows) (push) Successful in 42s
CI / build-and-package (arm64, darwin) (push) Successful in 45s
CI / build-and-package (arm64, linux) (push) Successful in 41s
Gitea: при параллельных PUT матрицы (6 платформ) псевдо-версия 'latest'
переуказывается на каждый снимок, companion-файлы и бинарь разъезжаются
между версиями — checksum U4 ломался.
- CI: публикует бинарь+.version+.sha256 в (commit-<sha7>), latest не используется
- Updater.ResolveLatest: новейшая commit-* версия из листинга /api/v1/packages/{owner}/generic/{package},
берём макс id у которой есть бинарь текущей платформы
- Download/Verify/checksum читают ТУ ЖЕ version, а не latest
This commit is contained in:
@@ -66,26 +66,6 @@ jobs:
|
|||||||
run: ls -lh "$FILENAME"
|
run: ls -lh "$FILENAME"
|
||||||
|
|
||||||
- name: Upload to Gitea Packages
|
- name: Upload to Gitea Packages
|
||||||
if: gitea.ref == 'refs/heads/main'
|
|
||||||
run: |
|
|
||||||
curl -sS -X PUT \
|
|
||||||
-H "Authorization: token ${{ secrets.TC_GITEA_TOKEN }}" \
|
|
||||||
-H "Content-Type: application/octet-stream" \
|
|
||||||
"${{ vars.GIT_MAIN_URL }}/api/packages/${{ gitea.actor }}/generic/ratatoskr/${VERSION}/${FILENAME}" \
|
|
||||||
--data-binary @${FILENAME}
|
|
||||||
echo "Uploaded ${VERSION}/${FILENAME}"
|
|
||||||
|
|
||||||
- name: Update latest tag
|
|
||||||
if: gitea.ref == 'refs/heads/main'
|
|
||||||
run: |
|
|
||||||
curl -sS -X PUT \
|
|
||||||
-H "Authorization: token ${{ secrets.TC_GITEA_TOKEN }}" \
|
|
||||||
-H "Content-Type: application/octet-stream" \
|
|
||||||
"${{ vars.GIT_MAIN_URL }}/api/packages/${{ gitea.actor }}/generic/ratatoskr/latest/${FILENAME}" \
|
|
||||||
--data-binary @${FILENAME}
|
|
||||||
echo "Updated 'latest/${FILENAME}'"
|
|
||||||
|
|
||||||
- name: Publish companion files (version + sha256)
|
|
||||||
if: gitea.ref == 'refs/heads/main'
|
if: gitea.ref == 'refs/heads/main'
|
||||||
run: |
|
run: |
|
||||||
SHA256_HEX=$(sha256sum "${FILENAME}" | awk '{print $1}')
|
SHA256_HEX=$(sha256sum "${FILENAME}" | awk '{print $1}')
|
||||||
@@ -93,14 +73,14 @@ jobs:
|
|||||||
printf '%s' "${VERSION}" > "${FILENAME}.version"
|
printf '%s' "${VERSION}" > "${FILENAME}.version"
|
||||||
# <filename>.sha256 — контрольная сумма бинаря (проверка U4)
|
# <filename>.sha256 — контрольная сумма бинаря (проверка U4)
|
||||||
printf '%s' "${SHA256_HEX}" > "${FILENAME}.sha256"
|
printf '%s' "${SHA256_HEX}" > "${FILENAME}.sha256"
|
||||||
|
# Всё публикуется в КОНКРЕТНУЮ версию ${VERSION}, НЕ в псевдо-version "latest".
|
||||||
|
# "latest" в Gitea при параллельных PUT матрицы (6 платформ) переуказывается
|
||||||
|
# на каждый снимок — companion-файлы и бинарь разъезжаются, ломая checksum (U4).
|
||||||
|
for f in "${FILENAME}" "${FILENAME}.version" "${FILENAME}.sha256"; do
|
||||||
curl -sS -X PUT \
|
curl -sS -X PUT \
|
||||||
-H "Authorization: token ${{ secrets.TC_GITEA_TOKEN }}" \
|
-H "Authorization: token ${{ secrets.TC_GITEA_TOKEN }}" \
|
||||||
-H "Content-Type: text/plain" \
|
-H "Content-Type: application/octet-stream" \
|
||||||
"${{ vars.GIT_MAIN_URL }}/api/packages/${{ gitea.actor }}/generic/ratatoskr/latest/${FILENAME}.version" \
|
"${{ vars.GIT_MAIN_URL }}/api/packages/${{ gitea.actor }}/generic/ratatoskr/${VERSION}/${f}" \
|
||||||
--data-binary @${FILENAME}.version
|
--data-binary @"${f}"
|
||||||
curl -sS -X PUT \
|
done
|
||||||
-H "Authorization: token ${{ secrets.TC_GITEA_TOKEN }}" \
|
echo "Published ${VERSION}/{binary,.version,.sha256}"
|
||||||
-H "Content-Type: text/plain" \
|
|
||||||
"${{ vars.GIT_MAIN_URL }}/api/packages/${{ gitea.actor }}/generic/ratatoskr/latest/${FILENAME}.sha256" \
|
|
||||||
--data-binary @${FILENAME}.sha256
|
|
||||||
echo "Published companion files for latest/${FILENAME}"
|
|
||||||
@@ -406,7 +406,7 @@ func (a *App) handleUpdateCmd(ctx context.Context, uid chat.UserID) {
|
|||||||
a.send(ctx, uid, "Ошибка скачивания: "+err.Error())
|
a.send(ctx, uid, "Ошибка скачивания: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := a.Updater.Verify(ctx, file); err != nil {
|
if err := a.Updater.Verify(ctx, res.Version, file); err != nil {
|
||||||
a.send(ctx, uid, "Обновление отклонено (контрольная сумма): "+err.Error())
|
a.send(ctx, uid, "Обновление отклонено (контрольная сумма): "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
// Package update — система автообновления бинаря ratatoskr.
|
// Package update — система автообновления бинаря ratatoskr.
|
||||||
//
|
//
|
||||||
// Источник: Gitea Packages (generic), хранилище `ratatoskr`,
|
// Источник: Gitea Packages (generic), хранилище `ratatoskr`. Каждая сборка
|
||||||
// с тремя companion-файлами актуальной (latest) версии:
|
// публикуется в отдельную версию `commit-<sha7>` и содержит три файла:
|
||||||
// - <filename> сам бинарь
|
// - <filename> сам бинарь
|
||||||
// - <filename>.version текстовый идентификатор версии (commit-<sha7>)
|
// - <filename>.version текстовый идентификатор версии (commit-<sha7>)
|
||||||
// - <filename>.sha256 hex-контрольная сумма бинаря
|
// - <filename>.sha256 hex-контрольная сумма бинаря
|
||||||
//
|
//
|
||||||
|
// Намеренно НЕ используется псевдо-версия `latest`: в Gitea при параллельных
|
||||||
|
// PUT (матрица CI: 6 платформ) она «переуказывается» на каждый снимок, и файлы
|
||||||
|
// одного релиза разъезжаются между разными версиями. Вместо этого новейшая
|
||||||
|
// версия определяется списком — GET /api/v1/packages/{owner}/generic/{package}
|
||||||
|
// отдаёт массив версий (id, version, created_at), берём новый `commit-*`.
|
||||||
|
//
|
||||||
// Модель обновления: «только по команде /update». Авто-режим (по расписанию
|
// Модель обновления: «только по команде /update». Авто-режим (по расписанию
|
||||||
// и при старте) делает ТОЛЬКО Check и уведомляет владельца в чат — ничего не
|
// и при старте) делает ТОЛЬКО Check и уведомляет владельца в чат — ничего не
|
||||||
// качает и не трогает работающий процесс.
|
// качает и не трогает работающий процесс.
|
||||||
@@ -15,10 +21,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -46,9 +54,9 @@ type Updater struct {
|
|||||||
|
|
||||||
// Result — результат Check.
|
// Result — результат Check.
|
||||||
type Result struct {
|
type Result struct {
|
||||||
// UpdateAvailable — true, если latest отличается от вшитой версии.
|
// UpdateAvailable — true, если новейшая версия отличается от вшитой.
|
||||||
UpdateAvailable bool
|
UpdateAvailable bool
|
||||||
// Version — идентификатор свежей (latest) версии.
|
// Version — идентификатор новейшей применимой версии (commit-<sha7>).
|
||||||
Version string
|
Version string
|
||||||
// Err — ошибка проверки (U1), нефатальная. Если != nil, остальные поля —
|
// Err — ошибка проверки (U1), нефатальная. Если != nil, остальные поля —
|
||||||
// нулевые, и качать нельзя.
|
// нулевые, и качать нельзя.
|
||||||
@@ -97,10 +105,17 @@ func Filename(goos, goarch string) string {
|
|||||||
// PlatformFilename — имя бинаря для текущей платформы.
|
// PlatformFilename — имя бинаря для текущей платформы.
|
||||||
func PlatformFilename() string { return Filename(runtime.GOOS, runtime.GOARCH) }
|
func PlatformFilename() string { return Filename(runtime.GOOS, runtime.GOARCH) }
|
||||||
|
|
||||||
// fileURL собирает URL companion-файла (версии/суммы) в хранилище latest.
|
// fileURL собирает URL файла в хранилище конкретной версии (НЕ latest).
|
||||||
func (u *Updater) fileURL(name string) string {
|
func (u *Updater) fileURL(version, name string) string {
|
||||||
base := strings.TrimRight(u.BaseURL, "/")
|
base := strings.TrimRight(u.BaseURL, "/")
|
||||||
return base + "/api/packages/" + u.Owner + "/generic/" + u.Package + "/latest/" + name
|
return base + "/api/packages/" + url.PathEscape(u.Owner) + "/generic/" +
|
||||||
|
url.PathEscape(u.Package) + "/" + url.PathEscape(version) + "/" + url.PathEscape(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// versionsURL — URL листинга версий пакета.
|
||||||
|
func (u *Updater) versionsURL() string {
|
||||||
|
base := strings.TrimRight(u.BaseURL, "/")
|
||||||
|
return base + "/api/v1/packages/" + url.PathEscape(u.Owner) + "/generic/" + url.PathEscape(u.Package)
|
||||||
}
|
}
|
||||||
|
|
||||||
// httpGet скачивает файл по URL бэкенда. При Token непустом — Basic/токен-заголовок.
|
// httpGet скачивает файл по URL бэкенда. При Token непустом — Basic/токен-заголовок.
|
||||||
@@ -123,37 +138,68 @@ func (u *Updater) httpGet(url string) ([]byte, error) {
|
|||||||
return io.ReadAll(resp.Body)
|
return io.ReadAll(resp.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check определяет, есть ли более свежая версия в хранилище latest.
|
// Check определяет, есть ли более свежая версия в Gitea Packages.
|
||||||
// Не скачивает бинарь. Ошибка (U1) возвращается в Result.Err — вызывающий
|
// Не скачивает бинарь. Ошибка (U1) возвращается в Result.Err — вызывающий
|
||||||
// решает, логировать и пропустить.
|
// решает, логировать и пропустить.
|
||||||
func (u *Updater) Check(ctx context.Context) Result {
|
func (u *Updater) Check(ctx context.Context) Result {
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return Result{Err: ue(U1, "check", err)}
|
return Result{Err: ue(U1, "check", err)}
|
||||||
}
|
}
|
||||||
ver, err := u.latestVersion(ctx)
|
ver, err := u.ResolveLatest(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Result{Err: ue(U1, "check", err)}
|
return Result{Err: ue(U1, "check", err)}
|
||||||
}
|
}
|
||||||
if ver == "" {
|
if ver == "" {
|
||||||
return Result{Err: ue(U1, "check", errors.New("latest version file empty"))}
|
return Result{Err: ue(U1, "check", errors.New("no applicable commit-* version found"))}
|
||||||
}
|
}
|
||||||
avail := u.CurrentVersion != "" && ver != u.CurrentVersion
|
avail := u.CurrentVersion != "" && ver != u.CurrentVersion
|
||||||
return Result{UpdateAvailable: avail, Version: ver}
|
return Result{UpdateAvailable: avail, Version: ver}
|
||||||
}
|
}
|
||||||
|
|
||||||
// latestVersion читает companion-файл <filename>.version хранилища latest.
|
// pkgVersion — одна запись из сплющенного листинга версий Gitea.
|
||||||
func (u *Updater) latestVersion(ctx context.Context) (string, error) {
|
// GET /api/v1/packages/{owner}/generic/{package} возвращает массив таких:
|
||||||
b, err := u.httpGet(u.fileURL(PlatformFilename() + ".version"))
|
// каждая запись (id, version, html_url) — отдельная версия пакета.
|
||||||
|
type pkgVersion struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Ver string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveLatest определяет идентификатор новейшей применимой версии пакета.
|
||||||
|
// Бинарь/метаданные читаем из КОНКРЕТНОЙ версии, а не из pseudo-`latest`,
|
||||||
|
// чтобы companion-файлы и бинарь всегда брались из одного снимка.
|
||||||
|
func (u *Updater) ResolveLatest(ctx context.Context) (string, error) {
|
||||||
|
b, err := u.httpGet(u.versionsURL())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(string(b)), nil
|
var vers []pkgVersion
|
||||||
|
if err := json.Unmarshal(b, &vers); err != nil {
|
||||||
|
return "", ue(U1, "list "+u.versionsURL(), err)
|
||||||
|
}
|
||||||
|
want := PlatformFilename()
|
||||||
|
// выбираем самую свежую версию (макс. id) класса commit-*, в которой есть бинарь.
|
||||||
|
best := ""
|
||||||
|
var bestID int64
|
||||||
|
for _, v := range vers {
|
||||||
|
if !strings.HasPrefix(v.Ver, "commit-") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// версия применима, только если в ней опубликован бинарь нашей платформы
|
||||||
|
if _, err := u.httpGet(u.fileURL(v.Ver, want)); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v.ID > bestID {
|
||||||
|
bestID = v.ID
|
||||||
|
best = v.Ver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download скачивает бинарь latest во временный файл и возвращает путь к нему.
|
// Download скачивает бинарь конкретной версии во временный файл и возвращает
|
||||||
// Поток проверки: метаданные уже получены Check. Файл: <Dir>/.ratatoskr.<ver>.new.
|
// путь к нему. Файл: <Dir>/.ratatoskr.<ver>.new.
|
||||||
func (u *Updater) Download(ctx context.Context, version string) (string, error) {
|
func (u *Updater) Download(ctx context.Context, version string) (string, error) {
|
||||||
b, err := u.httpGet(u.fileURL(PlatformFilename()))
|
b, err := u.httpGet(u.fileURL(version, PlatformFilename()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err // уже U3
|
return "", err // уже U3
|
||||||
}
|
}
|
||||||
@@ -168,9 +214,9 @@ func (u *Updater) Download(ctx context.Context, version string) (string, error)
|
|||||||
return tmp, nil
|
return tmp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// latestSum256 читает companion-файл контрольной суммы latest.
|
// versionSum256 читает companion-файл контрольной суммы конкретной версии.
|
||||||
func (u *Updater) latestSum256(ctx context.Context) (string, error) {
|
func (u *Updater) versionSum256(ctx context.Context, version string) (string, error) {
|
||||||
b, err := u.httpGet(u.fileURL(PlatformFilename() + ".sha256"))
|
b, err := u.httpGet(u.fileURL(version, PlatformFilename()+".sha256"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -180,13 +226,13 @@ func (u *Updater) latestSum256(ctx context.Context) (string, error) {
|
|||||||
// sha256hex — hex-сумма байтов.
|
// sha256hex — hex-сумма байтов.
|
||||||
func sha256hex(b []byte) string { h := sha256.Sum256(b); return hex.EncodeToString(h[:]) }
|
func sha256hex(b []byte) string { h := sha256.Sum256(b); return hex.EncodeToString(h[:]) }
|
||||||
|
|
||||||
// Verify проверяет контрольную сумму скачанного файла против latest-метаданных.
|
// Verify проверяет контрольную сумму скачанного файла против .sha256 ТОЙ ЖЕ версии.
|
||||||
func (u *Updater) Verify(ctx context.Context, file string) error {
|
func (u *Updater) Verify(ctx context.Context, version, file string) error {
|
||||||
b, err := os.ReadFile(file)
|
b, err := os.ReadFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ue(U4, "verify read", err)
|
return ue(U4, "verify read", err)
|
||||||
}
|
}
|
||||||
sum, err := u.latestSum256(ctx)
|
sum, err := u.versionSum256(ctx, version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package update
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
@@ -11,24 +12,53 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// mockGitea поднимает httptest-сервер с бинарём, <filename>.version и .sha256.
|
// mockGitea поднимает httptest-сервер: листинг версий `/api/v1/packages/...`
|
||||||
|
// и файлы конкретных версий `/api/packages/.../{version}/{filename}`.
|
||||||
|
// version — имя новейшей версии (id бóльший), У которой есть бинарь+метаданные;
|
||||||
|
// старые версии бинаря не содержат и отсеются ResolveLatest.
|
||||||
func mockGitea(t *testing.T, bin []byte, version, checksum string) *httptest.Server {
|
func mockGitea(t *testing.T, bin []byte, version, checksum string) *httptest.Server {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
name := PlatformFilename()
|
name := PlatformFilename()
|
||||||
mux := http.NewServeMux()
|
// старый commit, чтобы проверить, что Resolve берёт именно новейший.
|
||||||
mux.HandleFunc("/api/packages/", func(w http.ResponseWriter, r *http.Request) {
|
old := "commit-old" + version[len("commit-"):]
|
||||||
base := name
|
fileSets := map[string]map[string][]byte{
|
||||||
switch {
|
version: {
|
||||||
case strings.HasSuffix(r.URL.Path, ".version"):
|
name: bin,
|
||||||
base = name + ".version"
|
name + ".version": []byte(version),
|
||||||
_, _ = w.Write([]byte(version))
|
name + ".sha256": []byte(checksum),
|
||||||
case strings.HasSuffix(r.URL.Path, ".sha256"):
|
},
|
||||||
base = name + ".sha256"
|
|
||||||
_, _ = w.Write([]byte(checksum))
|
|
||||||
default:
|
|
||||||
_, _ = w.Write(bin) // сам бинарь
|
|
||||||
}
|
}
|
||||||
_ = base
|
versions := []map[string]any{
|
||||||
|
{"id": 1, "version": old},
|
||||||
|
{"id": 2, "version": version},
|
||||||
|
}
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/api/v1/packages/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodGet || !strings.HasSuffix(r.URL.Path, "/generic/ratatoskr") {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = json.NewEncoder(w).Encode(versions)
|
||||||
|
})
|
||||||
|
mux.HandleFunc("/api/packages/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodGet {
|
||||||
|
http.Error(w, "method", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// путь: /api/packages/{owner}/generic/{pkg}/{version}/{filename}
|
||||||
|
parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
|
||||||
|
if len(parts) < 6 {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ver := parts[len(parts)-2]
|
||||||
|
fileName := parts[len(parts)-1]
|
||||||
|
body, ok := fileSets[ver][fileName]
|
||||||
|
if !ok {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = w.Write(body)
|
||||||
})
|
})
|
||||||
return httptest.NewServer(mux)
|
return httptest.NewServer(mux)
|
||||||
}
|
}
|
||||||
@@ -113,7 +143,7 @@ func TestDownload_And_Verify_Good(t *testing.T) {
|
|||||||
if !strings.Contains(file, ".new") {
|
if !strings.Contains(file, ".new") {
|
||||||
t.Errorf("temp file %q should end with .new", filepath.Base(file))
|
t.Errorf("temp file %q should end with .new", filepath.Base(file))
|
||||||
}
|
}
|
||||||
if err := u.Verify(context.Background(), file); err != nil {
|
if err := u.Verify(context.Background(), "commit-new12345", file); err != nil {
|
||||||
t.Fatalf("Verify err = %v", err)
|
t.Fatalf("Verify err = %v", err)
|
||||||
}
|
}
|
||||||
// .new лежит в каталоге
|
// .new лежит в каталоге
|
||||||
@@ -128,11 +158,11 @@ func TestVerify_ChecksumMismatch(t *testing.T) {
|
|||||||
srv := mockGitea(t, bin, "commit-x", sha256hex([]byte("differente-content")))
|
srv := mockGitea(t, bin, "commit-x", sha256hex([]byte("differente-content")))
|
||||||
t.Cleanup(srv.Close)
|
t.Cleanup(srv.Close)
|
||||||
u := &Updater{BaseURL: srv.URL, Owner: "k", Package: "p", Dir: t.TempDir()}
|
u := &Updater{BaseURL: srv.URL, Owner: "k", Package: "p", Dir: t.TempDir()}
|
||||||
file := filepath.Join(u.Dir, ".ratatoskr.x.new")
|
file := filepath.Join(u.Dir, ".ratatoskr.commit-x.new")
|
||||||
if err := os.WriteFile(file, []byte("A"), 0o755); err != nil {
|
if err := os.WriteFile(file, []byte("A"), 0o755); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err := u.Verify(context.Background(), file)
|
err := u.Verify(context.Background(), "commit-x", file)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected checksum mismatch error")
|
t.Fatal("expected checksum mismatch error")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user