Files
Jonathan Miller 37322e4212
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Successful in 22s
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
feat(updates): manual release-to-stream mapping
Add release_stream_map table for explicitly assigning releases to
update streams. When a mapping exists, it overrides automatic tag
detection. When absent, falls back to tag name/suffix matching.

New model: ReleaseStreamMap with SetReleaseStream, GetReleaseStream,
ResolveReleaseStream (manual first, auto fallback).

UI: stream selector dropdown on release create/edit page, shown when
licensing is enabled. Options: auto-detect (default) or any
configured stream (stable, release-candidate, beta, etc.).

All three feed generators (Joomla, Dolibarr, WordPress) now use
ResolveReleaseStream instead of MatchStreamFromTag.

Migration v340 updated with release_stream_map table creation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-02 07:37:02 -05:00

89 lines
2.9 KiB
Go

// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package licenses
import (
"context"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/db"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/timeutil"
)
func init() {
db.RegisterModel(new(ReleaseStreamMap))
}
// ReleaseStreamMap manually assigns a release to an update stream.
// When present, overrides automatic stream detection from tag names.
type ReleaseStreamMap struct {
ID int64 `xorm:"pk autoincr"`
ReleaseID int64 `xorm:"UNIQUE NOT NULL INDEX"` // FK to release
RepoID int64 `xorm:"NOT NULL INDEX"` // for fast repo-scoped queries
StreamName string `xorm:"NOT NULL"` // e.g. "stable", "release-candidate"
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
}
func (ReleaseStreamMap) TableName() string {
return "release_stream_map"
}
// SetReleaseStream assigns or updates the stream for a release.
func SetReleaseStream(ctx context.Context, releaseID, repoID int64, streamName string) error {
existing := new(ReleaseStreamMap)
has, err := db.GetEngine(ctx).Where("release_id = ?", releaseID).Get(existing)
if err != nil {
return err
}
if has {
existing.StreamName = streamName
_, err = db.GetEngine(ctx).ID(existing.ID).Cols("stream_name").Update(existing)
return err
}
_, err = db.GetEngine(ctx).Insert(&ReleaseStreamMap{
ReleaseID: releaseID,
RepoID: repoID,
StreamName: streamName,
})
return err
}
// GetReleaseStream returns the manually assigned stream for a release, or empty string.
func GetReleaseStream(ctx context.Context, releaseID int64) string {
m := new(ReleaseStreamMap)
has, err := db.GetEngine(ctx).Where("release_id = ?", releaseID).Get(m)
if err != nil || !has {
return ""
}
return m.StreamName
}
// GetStreamMapForRepo returns all manual stream assignments for a repo.
func GetStreamMapForRepo(ctx context.Context, repoID int64) (map[int64]string, error) {
var maps []ReleaseStreamMap
if err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Find(&maps); err != nil {
return nil, err
}
result := make(map[int64]string, len(maps))
for _, m := range maps {
result[m.ReleaseID] = m.StreamName
}
return result, nil
}
// ResolveReleaseStream returns the stream for a release: manual mapping first, auto-detect fallback.
func ResolveReleaseStream(ctx context.Context, releaseID int64, tagName string, isPrerelease bool, streams []StreamDef) string {
if manual := GetReleaseStream(ctx, releaseID); manual != "" {
return manual
}
return MatchStreamFromTag(tagName, isPrerelease, streams)
}
// DeleteReleaseStream removes the manual stream assignment for a release.
func DeleteReleaseStream(ctx context.Context, releaseID int64) error {
_, err := db.GetEngine(ctx).Where("release_id = ?", releaseID).Delete(new(ReleaseStreamMap))
return err
}