| 1 | package release |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "crypto/ed25519" |
| 6 | "os" |
| 7 | "time" |
| 8 | |
| 9 | "github.com/carlosframework/platform/internal/registry" |
| 10 | ) |
| 11 | |
| 12 | type RootUpdateInput struct { |
| 13 | Begin func(version, digest, attempt string) (func(invocation string, failure error, verify func() error) error, error) |
| 14 | Route registry.Route |
| 15 | Build BuildStateCopy |
| 16 | Consumed *Consumed |
| 17 | Installed string |
| 18 | Failed func(string) bool |
| 19 | Save func(Consumed) error |
| 20 | } |
| 21 | |
| 22 | type RootUpdateInputs struct { |
| 23 | Resolve func(context.Context, string) (RootUpdateInput, error) |
| 24 | Close func() error |
| 25 | selected map[string]RootUpdateInput |
| 26 | } |
| 27 | |
| 28 | type RootUpdateOpener func(context.Context, Store, ed25519.PublicKey) (*RootUpdateInputs, error) |
| 29 | |
| 30 | func WithRootUpdateInputs(open RootUpdateOpener) PassOption { |
| 31 | return func(c *passConfig) { c.openRootUpdates = open } |
| 32 | } |
| 33 | |
| 34 | func (r *RootUpdateInputs) routes(ctx context.Context, rows []registry.Route) ([]registry.Route, error) { |
| 35 | if r.Resolve == nil { |
| 36 | return nil, os.ErrPermission |
| 37 | } |
| 38 | r.selected = map[string]RootUpdateInput{} |
| 39 | out := make([]registry.Route, 0, len(rows)) |
| 40 | for _, hint := range rows { |
| 41 | in, err := r.Resolve(ctx, hint.Host) |
| 42 | if err != nil { |
| 43 | return nil, err |
| 44 | } |
| 45 | if in.Route.Host != hint.Host || in.Failed == nil || in.Save == nil { |
| 46 | return nil, os.ErrPermission |
| 47 | } |
| 48 | r.selected[hint.Host] = in |
| 49 | out = append(out, in.Route) |
| 50 | } |
| 51 | return out, nil |
| 52 | } |
| 53 | |
| 54 | 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) { |
| 55 | in, err := r.Resolve(ctx, rt.Host) |
| 56 | if err != nil { |
| 57 | return "", TargetDecision{}, true |
| 58 | } |
| 59 | selected, ok := r.selected[rt.Host] |
| 60 | if !ok || in.Route != selected.Route { |
| 61 | return "", TargetDecision{}, true |
| 62 | } |
| 63 | u, err := ParseUpdateSettingsLenient(in.Route.UpdateSettings) |
| 64 | if err != nil { |
| 65 | return "", TargetDecision{}, true |
| 66 | } |
| 67 | target := TargetInput{Route: in.Route, Pointer: ptr, Settings: u, Build: in.Build, Consumed: in.Consumed, Installed: in.Installed, Failed: in.Failed} |
| 68 | if ptr != nil { |
| 69 | target.HeadHeld = Hold(bake, rt.Account, rt.App, rt.Channel, ptr.Version, holds, ignore || ptr.FastTrack, now) |
| 70 | target.HeadTag = cfg.tags[rt.Account+"/"+rt.App][ptr.Version] |
| 71 | } |
| 72 | dec := Target(target) |
| 73 | if err := in.Save(NextConsumed(target, dec)); err != nil { |
| 74 | return "", dec, true |
| 75 | } |
| 76 | return dec.Version, dec, dec.Version == "" |
| 77 | } |
| 78 | |
| 79 | func CombinePassOptions(options ...PassOption) PassOption { |
| 80 | return func(c *passConfig) { |
| 81 | for _, option := range options { |
| 82 | option(c) |
| 83 | } |
| 84 | } |
| 85 | } |
| 86 | |