90be636d66
Gitea registry client with tag listing and pattern matching, cron-based polling scheduler with first-poll safety, poll state persistence. DeployTriggerer interface for decoupled deploy triggering.
79 lines
2.4 KiB
Go
79 lines
2.4 KiB
Go
package registry
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"path"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// Client defines the interface for interacting with a container image registry.
|
|
type Client interface {
|
|
// ListTags returns all available tags for the given image.
|
|
ListTags(ctx context.Context, image string) ([]string, error)
|
|
|
|
// GetLatestTag returns the most recently created tag that matches the given
|
|
// glob pattern. Returns an empty string and no error if no tags match.
|
|
GetLatestTag(ctx context.Context, image string, pattern string) (string, error)
|
|
}
|
|
|
|
// DeployTriggerer is called by the poller when a new tag is detected for a
|
|
// stage with auto_deploy enabled. This decouples the registry package from the
|
|
// deployer implementation.
|
|
type DeployTriggerer interface {
|
|
TriggerDeploy(ctx context.Context, projectID, stageID, imageTag string) error
|
|
}
|
|
|
|
// MatchTags filters a list of tags, returning only those that match the given
|
|
// glob pattern. Pattern matching uses path.Match semantics (*, ?, []).
|
|
// Returns an error if the pattern is malformed.
|
|
func MatchTags(tags []string, pattern string) ([]string, error) {
|
|
if pattern == "" || pattern == "*" {
|
|
result := make([]string, len(tags))
|
|
copy(result, tags)
|
|
return result, nil
|
|
}
|
|
|
|
// Validate pattern once before iterating.
|
|
if _, err := path.Match(pattern, ""); err != nil {
|
|
return nil, fmt.Errorf("invalid tag pattern %q: %w", pattern, err)
|
|
}
|
|
|
|
var matched []string
|
|
for _, tag := range tags {
|
|
ok, _ := path.Match(pattern, tag)
|
|
if ok {
|
|
matched = append(matched, tag)
|
|
}
|
|
}
|
|
return matched, nil
|
|
}
|
|
|
|
// LatestTag returns the last element of a sorted tag list that matches the
|
|
// pattern. Tags are sorted lexicographically; the "latest" is the last in sort
|
|
// order. Returns empty string if no tags match. Returns an error if the pattern
|
|
// is malformed.
|
|
func LatestTag(tags []string, pattern string) (string, error) {
|
|
matched, err := MatchTags(tags, pattern)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(matched) == 0 {
|
|
return "", nil
|
|
}
|
|
sort.Strings(matched)
|
|
return matched[len(matched)-1], nil
|
|
}
|
|
|
|
// NewClient creates a registry Client based on the registry type string.
|
|
// Supported types: "gitea". Future: "github", "dockerhub".
|
|
func NewClient(registryType, baseURL, token string) (Client, error) {
|
|
switch strings.ToLower(registryType) {
|
|
case "gitea":
|
|
return NewGiteaClient(baseURL, token), nil
|
|
default:
|
|
return nil, fmt.Errorf("unsupported registry type: %s", registryType)
|
|
}
|
|
}
|