package release import ( "context" "crypto/ed25519" "os" "time" "github.com/carlosframework/platform/internal/registry" ) type RootUpdateInput struct { Begin func(version, digest, attempt string) (func(invocation string, failure error, verify func() error) error, error) Route registry.Route Build BuildStateCopy Consumed *Consumed Installed string Failed func(string) bool Save func(Consumed) error } type RootUpdateInputs struct { Resolve func(context.Context, string) (RootUpdateInput, error) Close func() error selected map[string]RootUpdateInput } type RootUpdateOpener func(context.Context, Store, ed25519.PublicKey) (*RootUpdateInputs, error) func WithRootUpdateInputs(open RootUpdateOpener) PassOption { return func(c *passConfig) { c.openRootUpdates = open } } func (r *RootUpdateInputs) routes(ctx context.Context, rows []registry.Route) ([]registry.Route, error) { if r.Resolve == nil { return nil, os.ErrPermission } r.selected = map[string]RootUpdateInput{} out := make([]registry.Route, 0, len(rows)) for _, hint := range rows { in, err := r.Resolve(ctx, hint.Host) if err != nil { return nil, err } if in.Route.Host != hint.Host || in.Failed == nil || in.Save == nil { return nil, os.ErrPermission } r.selected[hint.Host] = in out = append(out, in.Route) } return out, nil } func (r *RootUpdateInputs) target(ctx context.Context, rt registry.Route, ptr *ChannelPointer, bake *FirstSeen, now time.Time, cfg passConfig, holds map[string]time.Duration, ignore bool) (string, TargetDecision, bool) { in, err := r.Resolve(ctx, rt.Host) if err != nil { return "", TargetDecision{}, true } selected, ok := r.selected[rt.Host] if !ok || in.Route != selected.Route { return "", TargetDecision{}, true } u, err := ParseUpdateSettingsLenient(in.Route.UpdateSettings) if err != nil { return "", TargetDecision{}, true } target := TargetInput{Route: in.Route, Pointer: ptr, Settings: u, Build: in.Build, Consumed: in.Consumed, Installed: in.Installed, Failed: in.Failed} if ptr != nil { target.HeadHeld = Hold(bake, rt.Account, rt.App, rt.Channel, ptr.Version, holds, ignore || ptr.FastTrack, now) target.HeadTag = cfg.tags[rt.Account+"/"+rt.App][ptr.Version] } dec := Target(target) if err := in.Save(NextConsumed(target, dec)); err != nil { return "", dec, true } return dec.Version, dec, dec.Version == "" } func CombinePassOptions(options ...PassOption) PassOption { return func(c *passConfig) { for _, option := range options { option(c) } } }