From be5c2d35a54e08ab45d2282baacad3c14e079756 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 25 May 2026 17:53:13 -0500 Subject: [PATCH 1/8] chore: bump updates.xml to v05.00.00 Co-Authored-By: Claude Opus 4.6 (1M context) --- updates.xml | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/updates.xml b/updates.xml index 28bdb0ba1d..02e6b7a222 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ @@ -10,12 +10,12 @@ MokoGitea update mokogitea application - 04.01.00 + 05.00.00 server stable - https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.04.00.00 + https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.05.00.00 - git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.04.00.00 + git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.05.00.00 @@ -27,7 +27,7 @@ MokoGitea update mokogitea application - 04.01.00 + 05.00.00 server dev https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/src/branch/dev @@ -44,33 +44,16 @@ MokoGitea update mokogitea application - 04.01.00 + 05.00.00 server security - https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.04.00.00 + https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.05.00.00 - git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.04.00.00 + git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.05.00.00 Moko Consulting https://mokoconsulting.tech - - MokoGitea - MokoGitea RC from PR #170 - mokogitea - application - 04.01.00-rc.170 - server - rc - https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/pulls/170 - - git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.04.01.00-rc.170 - - - - Moko Consulting - https://mokoconsulting.tech - -- 2.52.0 From 8ad1b8a110334a2d338db104e0b56f03346424cc Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 25 May 2026 17:55:28 -0500 Subject: [PATCH 2/8] chore: align update streams to standard channels (dev/alpha/beta/rc/stable) Matches the Joomla update server pattern used across all Moko repos. Removed the non-standard 'security' channel. All five standard channels now present in updates.xml. Co-Authored-By: Claude Opus 4.6 (1M context) --- modules/setting/setting.go | 2 +- updates.xml | 44 +++++++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/modules/setting/setting.go b/modules/setting/setting.go index f152a865a0..2fb301156d 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -32,7 +32,7 @@ var ( UpdateChecker = struct { Enabled bool Endpoint string - Channel string // stable, dev, security + Channel string // stable, rc, beta, alpha, development }{ Enabled: true, Endpoint: "https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/updates.xml", diff --git a/updates.xml b/updates.xml index 02e6b7a222..f0d08df501 100644 --- a/updates.xml +++ b/updates.xml @@ -29,10 +29,10 @@ application 05.00.00 server - dev - https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/src/branch/dev + rc + https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.05.00.00 - git.mokoconsulting.tech/mokoconsulting/mokogitea:latest-dev + git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.05.00.00 @@ -46,8 +46,8 @@ application 05.00.00 server - security - https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.05.00.00 + beta + https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.05.00.00 git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.05.00.00 @@ -56,4 +56,38 @@ Moko Consulting https://mokoconsulting.tech + + MokoGitea + MokoGitea update + mokogitea + application + 05.00.00 + server + alpha + https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.05.00.00 + + git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.05.00.00 + + + + Moko Consulting + https://mokoconsulting.tech + + + MokoGitea + MokoGitea update + mokogitea + application + 05.00.00 + server + development + https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/src/branch/dev + + git.mokoconsulting.tech/mokoconsulting/mokogitea:latest-dev + + + + Moko Consulting + https://mokoconsulting.tech + -- 2.52.0 From 8509932b4198d38235a59de8e382392bda7e1067 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 25 May 2026 17:58:39 -0500 Subject: [PATCH 3/8] feat: update channel selector dropdown on admin dashboard Add a dropdown on the admin dashboard to switch between update streams (stable, rc, beta, alpha, development) matching the Joomla pattern. Changes: - Admin dashboard shows channel selector with descriptions - POST handler validates and applies channel change in-memory - Triggers immediate re-check against updates.xml after switch - updates.xml has all 5 standard channels with descriptions Co-Authored-By: Claude Opus 4.6 (1M context) --- routers/web/admin/admin.go | 31 +++++++++++++++++++++++++++++++ templates/admin/dashboard.tmpl | 14 ++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go index 901947a10a..37f945db53 100644 --- a/routers/web/admin/admin.go +++ b/routers/web/admin/admin.go @@ -144,6 +144,14 @@ func Dashboard(ctx *context.Context) { ctx.Data["ReleaseURL"] = info.ReleaseURL ctx.Data["UpdateChannel"] = info.Channel ctx.Data["DockerImage"] = info.DockerImage + ctx.Data["CurrentUpdateChannel"] = setting.UpdateChecker.Channel + ctx.Data["UpdateChannels"] = []map[string]string{ + {"value": "stable", "label": "Stable", "desc": "Production-ready releases"}, + {"value": "rc", "label": "Release Candidate", "desc": "Pre-release builds from merged PRs"}, + {"value": "beta", "label": "Beta", "desc": "Feature-complete, under testing"}, + {"value": "alpha", "label": "Alpha", "desc": "Early access, may have rough edges"}, + {"value": "development", "label": "Development", "desc": "Latest dev branch, bleeding edge"}, + } updateSystemStatus() ctx.Data["SysStatus"] = sysStatus @@ -166,6 +174,29 @@ func DashboardPost(ctx *context.Context) { updateSystemStatus() ctx.Data["SysStatus"] = sysStatus + // Handle update channel change + if channel := ctx.FormString("update_channel"); channel != "" { + validChannels := []string{"stable", "rc", "beta", "alpha", "development"} + isValid := false + for _, v := range validChannels { + if channel == v { + isValid = true + break + } + } + if isValid { + setting.UpdateChecker.Channel = channel + go func() { + if err := updatechecker.CheckForUpdate(); err != nil { + log.Error("CheckForUpdate after channel change: %v", err) + } + }() + ctx.Flash.Success("Update channel changed to: " + channel) + } + ctx.Redirect(setting.AppSubURL + "/-/admin") + return + } + // Run operation. if form.Op != "" { switch form.Op { diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl index bbe9ee62ea..ed2a866df5 100644 --- a/templates/admin/dashboard.tmpl +++ b/templates/admin/dashboard.tmpl @@ -8,6 +8,20 @@ {{if .DockerImage}}

docker pull {{.DockerImage}}

{{end}} {{end}} +
+

{{svg "octicon-broadcast" 16}} Update Channel

+
+ {{.CsrfTokenHtml}} +
+ +
+ +
+

{{ctx.Locale.Tr "admin.dashboard.maintenance_operations"}}

-- 2.52.0 From 07827bcc2e42306e8a1400f3a8c542e4b3058333 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 25 May 2026 18:19:19 -0500 Subject: [PATCH 4/8] test: bump dev channel to 06.00.00-dev to test update checker Co-Authored-By: Claude Opus 4.6 (1M context) --- updates.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/updates.xml b/updates.xml index f0d08df501..8f21ddd206 100644 --- a/updates.xml +++ b/updates.xml @@ -78,12 +78,12 @@ MokoGitea update mokogitea application - 05.00.00 + 06.00.00-dev server development https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/src/branch/dev - git.mokoconsulting.tech/mokoconsulting/mokogitea:latest-dev + git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.06.00.00-dev -- 2.52.0 From 13352e721346bac7dedc2da613e2a8422ea60cda Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 25 May 2026 18:24:03 -0500 Subject: [PATCH 5/8] feat: email admin when MokoGitea update is detected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The update checker now emails the first admin user when a new version is found on the configured channel. Notifications are deduplicated — only sent once per new version, not on every cron tick. - Added NotifyFunc callback in updatechecker module - Wired to mailer in cron task registration - Created mail_update.go with plain-text email including version, channel, release URL, and docker pull command Co-Authored-By: Claude Opus 4.6 (1M context) --- modules/updatechecker/updatechecker.go | 19 +++++++-- services/cron/tasks_basic.go | 6 +++ services/mailer/mail_update.go | 53 ++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 services/mailer/mail_update.go diff --git a/modules/updatechecker/updatechecker.go b/modules/updatechecker/updatechecker.go index 61093db2f4..83cfbd37eb 100644 --- a/modules/updatechecker/updatechecker.go +++ b/modules/updatechecker/updatechecker.go @@ -26,9 +26,14 @@ type UpdateInfo struct { CheckedAt time.Time } +// NotifyFunc is called when a new update is detected for the first time. +// Set this from the cron/mailer layer to send admin email notifications. +var NotifyFunc func(info *UpdateInfo) + var ( - cachedInfo *UpdateInfo - mu sync.RWMutex + cachedInfo *UpdateInfo + lastNotifiedVer string + mu sync.RWMutex ) // xmlUpdates mirrors the updates.xml structure (Joomla-style). @@ -134,16 +139,22 @@ func CheckForUpdate() error { } // Update is available if the latest version string is not a prefix of the current version. - // e.g., current "1.26.1+305-gabcdef" does not start with "04.00.00" - // This handles both moko semver and git-describe suffixed versions. info.UpdateAvailable = latestVersion != "" && !strings.Contains(currentVersion, latestVersion) mu.Lock() cachedInfo = info + // Notify only once per new version (avoid spamming on every cron tick) + shouldNotify := info.UpdateAvailable && latestVersion != lastNotifiedVer + if shouldNotify { + lastNotifiedVer = latestVersion + } mu.Unlock() if info.UpdateAvailable { log.Info("MokoGitea update available: %s [%s] (current: %s)", latestVersion, channel, currentVersion) + if shouldNotify && NotifyFunc != nil { + NotifyFunc(info) + } } else { log.Debug("MokoGitea is up to date: %s [%s]", currentVersion, channel) } diff --git a/services/cron/tasks_basic.go b/services/cron/tasks_basic.go index 9b1788dfef..c91249e06d 100644 --- a/services/cron/tasks_basic.go +++ b/services/cron/tasks_basic.go @@ -15,6 +15,7 @@ import ( "git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/setting" "git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/updatechecker" "git.mokoconsulting.tech/MokoConsulting/MokoGitea/services/auth" + "git.mokoconsulting.tech/MokoConsulting/MokoGitea/services/mailer" "git.mokoconsulting.tech/MokoConsulting/MokoGitea/services/migrations" mirror_service "git.mokoconsulting.tech/MokoConsulting/MokoGitea/services/mirror" packages_cleanup_service "git.mokoconsulting.tech/MokoConsulting/MokoGitea/services/packages/cleanup" @@ -190,6 +191,11 @@ func initBasicTasks() { } func registerUpdateChecker() { + // Wire up email notification for admin when updates are detected + updatechecker.NotifyFunc = func(info *updatechecker.UpdateInfo) { + mailer.SendUpdateNotification(info.LatestVersion, info.Channel, info.ReleaseURL, info.DockerImage) + } + RegisterTaskFatal("update_checker", &BaseConfig{ Enabled: true, RunAtStart: true, diff --git a/services/mailer/mail_update.go b/services/mailer/mail_update.go new file mode 100644 index 0000000000..1b35106589 --- /dev/null +++ b/services/mailer/mail_update.go @@ -0,0 +1,53 @@ +// Copyright 2026 Moko Consulting +// SPDX-License-Identifier: GPL-3.0-or-later + +package mailer + +import ( + "context" + "fmt" + + user_model "git.mokoconsulting.tech/MokoConsulting/MokoGitea/models/user" + "git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log" + "git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/setting" + sender_service "git.mokoconsulting.tech/MokoConsulting/MokoGitea/services/mailer/sender" +) + +// SendUpdateNotification emails all admin users when a new MokoGitea version is available. +func SendUpdateNotification(version, channel, releaseURL, dockerImage string) { + if setting.MailService == nil { + log.Debug("Update notification skipped: mail service not configured") + return + } + + admin, err := user_model.GetAdminUser(context.Background()) + if err != nil { + log.Error("SendUpdateNotification: GetAdminUser: %v", err) + return + } + + subject := fmt.Sprintf("[MokoGitea] Update available: %s (%s)", version, channel) + + body := fmt.Sprintf(`MokoGitea Update Available + +A new version is available on the %s channel. + +Version: %s +Channel: %s +Current: %s`, channel, version, channel, setting.AppVer) + + if releaseURL != "" { + body += fmt.Sprintf("\nRelease: %s", releaseURL) + } + if dockerImage != "" { + body += fmt.Sprintf("\nDocker: docker pull %s", dockerImage) + } + + body += fmt.Sprintf("\n\nUpdate the channel in Site Administration > Dashboard.\n\n— %s", setting.AppName) + + msg := sender_service.NewMessage(admin.EmailTo(), subject, body) + msg.Info = "Update notification" + + SendAsync(msg) + log.Info("Update notification sent to %s for version %s [%s]", admin.Email, version, channel) +} -- 2.52.0 From 49fe3cf6ebec26f98ba2994f53e0dc73bdfd4ed2 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 25 May 2026 18:34:14 -0500 Subject: [PATCH 6/8] feat: add ntfy push notification for update checker Update notifications now go through three channels: - Admin dashboard banner (existing) - Email to admin (added in previous commit) - ntfy push notification (new) Configure in app.ini: [ntfy] ENABLED = true SERVER_URL = https://ntfy.mokoconsulting.tech DEFAULT_TOPIC = mokogitea TOKEN = (optional bearer token) Co-Authored-By: Claude Opus 4.6 (1M context) --- services/mailer/mail_update.go | 72 ++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/services/mailer/mail_update.go b/services/mailer/mail_update.go index 1b35106589..eb41a10b23 100644 --- a/services/mailer/mail_update.go +++ b/services/mailer/mail_update.go @@ -4,8 +4,11 @@ package mailer import ( + "bytes" "context" "fmt" + "io" + "net/http" user_model "git.mokoconsulting.tech/MokoConsulting/MokoGitea/models/user" "git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log" @@ -13,19 +16,8 @@ import ( sender_service "git.mokoconsulting.tech/MokoConsulting/MokoGitea/services/mailer/sender" ) -// SendUpdateNotification emails all admin users when a new MokoGitea version is available. +// SendUpdateNotification emails the admin and sends ntfy push when a new MokoGitea version is available. func SendUpdateNotification(version, channel, releaseURL, dockerImage string) { - if setting.MailService == nil { - log.Debug("Update notification skipped: mail service not configured") - return - } - - admin, err := user_model.GetAdminUser(context.Background()) - if err != nil { - log.Error("SendUpdateNotification: GetAdminUser: %v", err) - return - } - subject := fmt.Sprintf("[MokoGitea] Update available: %s (%s)", version, channel) body := fmt.Sprintf(`MokoGitea Update Available @@ -45,9 +37,57 @@ Current: %s`, channel, version, channel, setting.AppVer) body += fmt.Sprintf("\n\nUpdate the channel in Site Administration > Dashboard.\n\n— %s", setting.AppName) - msg := sender_service.NewMessage(admin.EmailTo(), subject, body) - msg.Info = "Update notification" + // Send email to admin + if setting.MailService != nil { + admin, err := user_model.GetAdminUser(context.Background()) + if err != nil { + log.Error("SendUpdateNotification: GetAdminUser: %v", err) + } else { + msg := sender_service.NewMessage(admin.EmailTo(), subject, body) + msg.Info = "Update notification" + SendAsync(msg) + log.Info("Update email sent to %s for version %s [%s]", admin.Email, version, channel) + } + } - SendAsync(msg) - log.Info("Update notification sent to %s for version %s [%s]", admin.Email, version, channel) + // Send ntfy push notification + if setting.Ntfy.Enabled && setting.Ntfy.ServerURL != "" { + sendNtfyNotification(subject, body, releaseURL) + } +} + +func sendNtfyNotification(title, body, clickURL string) { + url := fmt.Sprintf("%s/%s", setting.Ntfy.ServerURL, setting.Ntfy.DefaultTopic) + + req, err := http.NewRequest("POST", url, bytes.NewBufferString(body)) + if err != nil { + log.Error("ntfy: create request: %v", err) + return + } + + req.Header.Set("Title", title) + req.Header.Set("Priority", "high") + req.Header.Set("Tags", "arrow_up,mokogitea") + if clickURL != "" { + req.Header.Set("Click", clickURL) + } + if setting.Ntfy.Token != "" { + req.Header.Set("Authorization", "Bearer "+setting.Ntfy.Token) + } + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Error("ntfy: send notification: %v", err) + return + } + defer resp.Body.Close() + io.Copy(io.Discard, resp.Body) + + if resp.StatusCode >= 300 { + log.Error("ntfy: unexpected status %d", resp.StatusCode) + return + } + + log.Info("ntfy: update notification sent to %s/%s", setting.Ntfy.ServerURL, setting.Ntfy.DefaultTopic) } -- 2.52.0 From 90f612f211a3b6d8853506972c002fcae81088e1 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 25 May 2026 19:05:13 -0500 Subject: [PATCH 7/8] feat: auto-generate SHA256 checksums for release attachments When a release is created or updated with attachments, automatically compute SHA256 checksums for every file and attach a checksums.sha256 manifest file. The manifest follows the standard sha256sum format: Existing checksums.sha256 files are replaced when attachments change. Checksums are generated for both CreateRelease and UpdateRelease flows. Co-Authored-By: Claude Opus 4.6 (1M context) --- services/release/checksum.go | 82 ++++++++++++++++++++++++++++++++++++ services/release/release.go | 14 ++++++ 2 files changed, 96 insertions(+) create mode 100644 services/release/checksum.go diff --git a/services/release/checksum.go b/services/release/checksum.go new file mode 100644 index 0000000000..fd4f7ae0d5 --- /dev/null +++ b/services/release/checksum.go @@ -0,0 +1,82 @@ +// Copyright 2026 Moko Consulting +// SPDX-License-Identifier: GPL-3.0-or-later + +package release + +import ( + "bytes" + "context" + "crypto/sha256" + "fmt" + "io" + + repo_model "git.mokoconsulting.tech/MokoConsulting/MokoGitea/models/repo" + "git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log" + "git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/storage" + attachment_service "git.mokoconsulting.tech/MokoConsulting/MokoGitea/services/attachment" +) + +// GenerateReleaseChecksums computes SHA256 checksums for all attachments +// on a release and adds a checksums.sha256 manifest file as an attachment. +func GenerateReleaseChecksums(ctx context.Context, rel *repo_model.Release) error { + // Load attachments into rel.Attachments + if err := repo_model.GetReleaseAttachments(ctx, rel); err != nil { + return fmt.Errorf("GetReleaseAttachments: %w", err) + } + + if len(rel.Attachments) == 0 { + return nil + } + + // Remove existing checksums file if present + for _, a := range rel.Attachments { + if a.Name == "checksums.sha256" { + if err := repo_model.DeleteAttachment(ctx, a, true); err != nil { + log.Warn("Failed to delete old checksums.sha256: %v", err) + } + break + } + } + + // Compute SHA256 for each attachment + var manifest bytes.Buffer + for _, a := range rel.Attachments { + if a.Name == "checksums.sha256" { + continue + } + + fr, err := storage.Attachments.Open(a.RelativePath()) + if err != nil { + log.Warn("Cannot open attachment %s for checksumming: %v", a.Name, err) + continue + } + + h := sha256.New() + if _, err := io.Copy(h, fr); err != nil { + fr.Close() + log.Warn("Cannot read attachment %s for checksumming: %v", a.Name, err) + continue + } + fr.Close() + + fmt.Fprintf(&manifest, "%x %s\n", h.Sum(nil), a.Name) + } + + if manifest.Len() == 0 { + return nil + } + + // Create the checksums.sha256 attachment + checksumAttach := &repo_model.Attachment{ + RepoID: rel.RepoID, + ReleaseID: rel.ID, + Name: "checksums.sha256", + } + + if _, err := attachment_service.NewAttachment(ctx, checksumAttach, &manifest, int64(manifest.Len())); err != nil { + return fmt.Errorf("create checksums.sha256 attachment: %w", err) + } + + log.Info("Generated checksums.sha256 for release %s (repo %d)", rel.TagName, rel.RepoID) + return nil +} diff --git a/services/release/release.go b/services/release/release.go index 5afc6cbe22..c54a06f7e6 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -190,6 +190,13 @@ func CreateRelease(gitRepo *git.Repository, rel *repo_model.Release, attachmentU return err } + // Generate SHA256 checksums for all release attachments + if len(attachmentUUIDs) > 0 { + if err := GenerateReleaseChecksums(gitRepo.Ctx, rel); err != nil { + log.Error("GenerateReleaseChecksums for %s: %v", rel.TagName, err) + } + } + if !rel.IsDraft { notify_service.NewRelease(gitRepo.Ctx, rel) } @@ -344,6 +351,13 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo } } + // Regenerate checksums when attachments change + if len(addAttachmentUUIDs) > 0 || len(delAttachmentUUIDs) > 0 { + if err := GenerateReleaseChecksums(ctx, rel); err != nil { + log.Error("GenerateReleaseChecksums for %s: %v", rel.TagName, err) + } + } + if !rel.IsDraft { if !isTagCreated && !isConvertedFromTag { notify_service.UpdateRelease(gitRepo.Ctx, doer, rel) -- 2.52.0 From a847129f9c8ee8064f9233ca49fe24ce42765da7 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 25 May 2026 19:15:12 -0500 Subject: [PATCH 8/8] fix: generate checksums on API asset upload, not just CreateRelease The API endpoint POST /releases/{id}/assets bypasses CreateRelease and UpdateRelease, so checksums were not generated for API uploads. Added GenerateReleaseChecksums call after successful asset upload. Co-Authored-By: Claude Opus 4.6 (1M context) --- routers/api/v1/repo/release_attachment.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go index f46f244768..4dd63e2f27 100644 --- a/routers/api/v1/repo/release_attachment.go +++ b/routers/api/v1/repo/release_attachment.go @@ -18,6 +18,7 @@ import ( "git.mokoconsulting.tech/MokoConsulting/MokoGitea/services/context" "git.mokoconsulting.tech/MokoConsulting/MokoGitea/services/context/upload" "git.mokoconsulting.tech/MokoConsulting/MokoGitea/services/convert" + release_service "git.mokoconsulting.tech/MokoConsulting/MokoGitea/services/release" ) func checkReleaseMatchRepo(ctx *context.APIContext, releaseID int64) bool { @@ -263,6 +264,14 @@ func CreateReleaseAttachment(ctx *context.APIContext) { return } + // Regenerate checksums after new attachment + rel, relErr := repo_model.GetReleaseByID(ctx, releaseID) + if relErr == nil { + if checksumErr := release_service.GenerateReleaseChecksums(ctx, rel); checksumErr != nil { + log.Error("GenerateReleaseChecksums after upload: %v", checksumErr) + } + } + ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) } -- 2.52.0