From 90ba3c09eb4890226e4d7198be3d79b20b5ef5ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=28=D0=A5?= =?UTF-8?q?=D1=80=D0=B0=D0=BC=D1=8B=D1=87=D0=AA=29=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Thu, 21 Aug 2025 14:41:08 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC=D0=B0=D1=82?= =?UTF-8?q?=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=BE=D0=B3=D0=BE=20=D0=BE=D0=B1?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20checksums?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/builtins/checksum.go | 75 ++++++++++++++++++++++++++++ internal/builtins/register.go | 1 + internal/builtins/updater.go | 94 +++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 internal/builtins/checksum.go diff --git a/internal/builtins/checksum.go b/internal/builtins/checksum.go new file mode 100644 index 0000000..8c4b99e --- /dev/null +++ b/internal/builtins/checksum.go @@ -0,0 +1,75 @@ +/* + * ALR Updater - Automated updater bot for ALR packages + * Copyright (C) 2025 The ALR Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package builtins + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "net/http" + "time" + + "go.starlark.net/starlark" + "go.starlark.net/starlarkstruct" +) + +func checksumModule() *starlarkstruct.Module { + return &starlarkstruct.Module{ + Name: "checksum", + Members: starlark.StringDict{ + "calculate_sha256": starlark.NewBuiltin("checksum.calculate_sha256", calculateSHA256), + }, + } +} + +func calculateSHA256(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var url string + err := starlark.UnpackArgs("checksum.calculate_sha256", args, kwargs, "url", &url) + if err != nil { + return nil, err + } + + // Создаем HTTP клиент с таймаутом + client := &http.Client{ + Timeout: 5 * time.Minute, + } + + // Загружаем файл + resp, err := client.Get(url) + if err != nil { + return nil, fmt.Errorf("failed to download file: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("bad status: %s", resp.Status) + } + + // Вычисляем SHA256 + hasher := sha256.New() + _, err = io.Copy(hasher, resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to calculate hash: %w", err) + } + + // Возвращаем хеш в виде hex строки + hashSum := hex.EncodeToString(hasher.Sum(nil)) + return starlark.String(hashSum), nil +} \ No newline at end of file diff --git a/internal/builtins/register.go b/internal/builtins/register.go index 8fa3143..08099ba 100644 --- a/internal/builtins/register.go +++ b/internal/builtins/register.go @@ -46,5 +46,6 @@ func Register(sd starlark.StringDict, opts *Options) { sd["json"] = starlarkjson.Module sd["utils"] = utilsModule sd["html"] = htmlModule + sd["checksum"] = checksumModule() sd["register_webhook"] = registerWebhook(opts.Mux, opts.Config, opts.Name) } diff --git a/internal/builtins/updater.go b/internal/builtins/updater.go index 634bf86..449b79a 100644 --- a/internal/builtins/updater.go +++ b/internal/builtins/updater.go @@ -19,6 +19,7 @@ package builtins import ( + "fmt" "io" "os" "path/filepath" @@ -45,6 +46,7 @@ func updaterModule(cfg *config.Config) *starlarkstruct.Module { "push_changes": updaterPushChanges(cfg), "get_package_file": getPackageFile(cfg), "write_package_file": writePackageFile(cfg), + "update_checksums": updateChecksums(cfg), }, } } @@ -235,3 +237,95 @@ func autoResetRelease(oldContent, newContent string) string { return newContent } + +// updateChecksumsInContent обновляет значения checksums в содержимом файла +func updateChecksumsInContent(content string, checksums []string) string { + // Паттерн для поиска массива checksums + checksumsRegex := regexp.MustCompile(`checksums=\((.*?)\)`) + + // Формируем новый массив checksums + var newChecksumsArray string + if len(checksums) == 1 && checksums[0] != "" { + // Если одна хеш-сумма, форматируем как ('hash') + newChecksumsArray = fmt.Sprintf("('%s')", checksums[0]) + } else if len(checksums) > 1 { + // Если несколько хеш-сумм, форматируем как ('hash1' 'hash2' ...) + quotedChecksums := make([]string, len(checksums)) + for i, cs := range checksums { + quotedChecksums[i] = fmt.Sprintf("'%s'", cs) + } + newChecksumsArray = "(" + strings.Join(quotedChecksums, " ") + ")" + } else { + // Если нет хеш-сумм, оставляем SKIP + newChecksumsArray = "('SKIP')" + } + + // Заменяем старый массив checksums на новый + newContent := checksumsRegex.ReplaceAllString(content, "checksums="+newChecksumsArray) + + return newContent +} + +func updateChecksums(cfg *config.Config) *starlark.Builtin { + return starlark.NewBuiltin("updater.update_checksums", func(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var pkg, filename, content string + var checksums *starlark.List + err := starlark.UnpackArgs("updater.update_checksums", args, kwargs, + "pkg", &pkg, + "filename", &filename, + "content", &content, + "checksums", &checksums) + if err != nil { + return nil, err + } + + // Конвертируем starlark.List в []string + checksumStrings := make([]string, 0, checksums.Len()) + iter := checksums.Iterate() + defer iter.Done() + var val starlark.Value + for iter.Next(&val) { + if s, ok := val.(starlark.String); ok { + checksumStrings = append(checksumStrings, string(s)) + } + } + + // Обновляем checksums в содержимом + updatedContent := updateChecksumsInContent(content, checksumStrings) + + // Автоматически сбрасываем release='1' при изменении версии + repoMtx.Lock() + defer repoMtx.Unlock() + + path := filepath.Join(cfg.Git.RepoDir, pkg, filename) + + // Читаем старый файл для сравнения версий + var oldContent string + if oldData, err := os.ReadFile(path); err == nil { + oldContent = string(oldData) + } + + // Применяем autoResetRelease + finalContent := autoResetRelease(oldContent, updatedContent) + + // Записываем файл + fl, err := os.Create(path) + if err != nil { + return nil, err + } + defer fl.Close() + + _, err = io.Copy(fl, strings.NewReader(finalContent)) + if err != nil { + return nil, err + } + + log.Debug("Updated package file with checksums"). + Str("package", pkg). + Str("filename", filename). + Int("checksums_count", len(checksumStrings)). + Stringer("pos", thread.CallFrame(1).Pos).Send() + + return starlark.None, nil + }) +}