549e890cd0
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Project CI / Lint & Validate (push) Successful in 40s
Deploy MokoGitea / deploy (push) Successful in 5m29s
Generic: Project CI / Tests (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Rename the licensing/update server model package to better reflect its purpose. All imports updated from licenses_model to updateserver_model. License key management UI files kept (only imports updated).
138 lines
3.9 KiB
Go
138 lines
3.9 KiB
Go
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package updateserver
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/db"
|
|
updateserver_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/updateserver"
|
|
repo_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/repo"
|
|
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/setting"
|
|
)
|
|
|
|
// DolibarrUpdate represents a single module update entry in Dolibarr format.
|
|
type DolibarrUpdate struct {
|
|
Name string `json:"name"`
|
|
Version string `json:"version"`
|
|
Channel string `json:"channel"`
|
|
DownloadURL string `json:"url"`
|
|
ChangelogURL string `json:"changelog"`
|
|
ReleaseURL string `json:"release_url"`
|
|
Requires string `json:"requires,omitempty"`
|
|
Date string `json:"date"`
|
|
SHA256 string `json:"sha256,omitempty"`
|
|
}
|
|
|
|
// DolibarrUpdates holds the full update feed response.
|
|
type DolibarrUpdates struct {
|
|
Module string `json:"module"`
|
|
Updates []DolibarrUpdate `json:"updates"`
|
|
}
|
|
|
|
// GenerateDolibarrJSON builds a Dolibarr-compatible update feed from releases.
|
|
// allowedChannels optionally restricts output to specific channels (nil = all).
|
|
func GenerateDolibarrJSON(ctx context.Context, repo *repo_model.Repository, allowedChannels ...string) (*DolibarrUpdates, error) {
|
|
releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{
|
|
RepoID: repo.ID,
|
|
ListOptions: db.ListOptionsAll,
|
|
IncludeDrafts: false,
|
|
IncludeTags: false,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("FindReleases: %w", err)
|
|
}
|
|
|
|
if err := repo.LoadOwner(ctx); err != nil {
|
|
return nil, fmt.Errorf("LoadOwner: %w", err)
|
|
}
|
|
|
|
baseURL := strings.TrimSuffix(setting.AppURL, "/")
|
|
repoLink := fmt.Sprintf("%s/%s/%s", baseURL, repo.Owner.Name, repo.Name)
|
|
|
|
result := &DolibarrUpdates{
|
|
Module: repo.Name,
|
|
}
|
|
|
|
// Resolve effective streams.
|
|
streams := updateserver_model.GetEffectiveStreams(ctx, repo.OwnerID, repo.ID)
|
|
|
|
// Track best release per channel.
|
|
bestByChannel := make(map[string]*repo_model.Release)
|
|
for _, rel := range releases {
|
|
if rel.IsDraft || rel.IsTag {
|
|
continue
|
|
}
|
|
ch := updateserver_model.ResolveReleaseStream(ctx, rel.ID, rel.TagName, rel.IsPrerelease, streams)
|
|
existing, ok := bestByChannel[ch]
|
|
if !ok || rel.CreatedUnix > existing.CreatedUnix {
|
|
bestByChannel[ch] = rel
|
|
}
|
|
}
|
|
|
|
// Build allowed channel set for filtering.
|
|
channelAllowed := make(map[string]bool)
|
|
if len(allowedChannels) > 0 {
|
|
for _, c := range allowedChannels {
|
|
channelAllowed[NormalizeChannel(c)] = true
|
|
}
|
|
}
|
|
|
|
for _, stream := range streams {
|
|
ch := stream.Name
|
|
if len(channelAllowed) > 0 && !channelAllowed[ch] {
|
|
continue
|
|
}
|
|
rel, ok := bestByChannel[ch]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
if err := rel.LoadAttributes(ctx); err != nil {
|
|
continue
|
|
}
|
|
|
|
var downloadURL string
|
|
for _, att := range rel.Attachments {
|
|
if strings.HasSuffix(strings.ToLower(att.Name), ".zip") {
|
|
downloadURL = fmt.Sprintf("%s/releases/download/%s/%s", repoLink, rel.TagName, att.Name)
|
|
break
|
|
}
|
|
}
|
|
if downloadURL == "" {
|
|
downloadURL = fmt.Sprintf("%s/archive/%s.zip", repoLink, rel.TagName)
|
|
}
|
|
|
|
version := extractVersion(rel.TagName)
|
|
if version == "" || isStreamName(rel.TagName, streams) {
|
|
version = extractVersion(rel.Title)
|
|
}
|
|
if version == "" {
|
|
version = rel.TagName
|
|
}
|
|
suffix := stream.Suffix
|
|
if suffix == "" {
|
|
suffix = channelSuffix(ch)
|
|
}
|
|
if suffix != "" {
|
|
version = version + suffix
|
|
}
|
|
|
|
result.Updates = append(result.Updates, DolibarrUpdate{
|
|
Name: repo.Name,
|
|
Version: version,
|
|
Channel: ch,
|
|
DownloadURL: downloadURL,
|
|
ChangelogURL: fmt.Sprintf("%s/raw/branch/%s/CHANGELOG.md", repoLink, repo.DefaultBranch),
|
|
ReleaseURL: fmt.Sprintf("%s/releases/tag/%s", repoLink, rel.TagName),
|
|
Date: time.Unix(int64(rel.CreatedUnix), 0).Format("2006-01-02"),
|
|
})
|
|
}
|
|
|
|
return result, nil
|
|
}
|