// Package forms holds the artifact reference forms the builder writes a done build key in and the assigner holds a want // by (novox/hq ADR 0618 §1-§2, ADR 0624): logic only those two modules of mesh-delivery share, so it is mesh-delivery's // and not mesh-sdk's (ADR 0593 §1, ADR 0608 §5). The build key's fields (delivery.BuilderBuild.Needs, // delivery.BuiltArtifact.References) are the contract, in mesh-sdk. package forms import ( "slices" "sort" "strings" "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/delivery" ) // artifactStore opens the artifact store's references (delivery.BuiltArtifact). const artifactStore = "artifact-store://" // The artifact reference forms (novox/hq ADR 0605 §9, ADR 0618 §1-§2, ADR 0624): each form a done key's reference may be // written in, named as the reader capability `form:` a node-engine states when it reads it. The forms written // before any engine stated what it reads are first forms: every engine reads them, so none needs a statement, and a // node whose engine states nothing (the baseline, ADR 0614 §4) reads exactly those. A new form is added to // Current, newest last, in the change that teaches the node-engine to read it. // The first forms, by name. const ( // FormArtifactStore: artifact-store:////blobs/, or an image's kept artifact-store://…@. FormArtifactStore = "artifact-store" // FormRegistry: an image's or an upstream copy's //@. FormRegistry = "registry" // FormPackageVersion: a package's @. FormPackageVersion = "package-version" ) // FormSeatReference is the form a registry seat answers for a bundle or an archive it published, // oci-artifact-registry//@sha256: (novox/hq ADR 0605 §9; catalogue.SeatReference, read back by // catalogue.InRegistrySeat), read by a node-engine that states form:seat-reference (catalogue.ReferenceFormSeat). Not a // first form: an engine stating nothing reads its blob only in the older artifact-store form, which it derives (Older). const FormSeatReference = catalogue.ReferenceFormSeat // ArtifactForm is one reference form: its name, whether it is a first form, the artifact kinds it names, how a // reference is told to be in it, and, for a form that names the same artifact an older form does, that older reference // (Older, nil when it derives none): the old-form window writes it beside the answered one (ADR 0624 §1, §3). type ArtifactForm struct { Name string First bool Kinds []string Is func(reference string) bool Older func(reference string) string } // ArtifactForms are the forms a builder knows, oldest first. type ArtifactForms []ArtifactForm // Current are the forms the builder writes and the assigner knows: the three first forms (delivery.BuiltArtifact's // reference by kind), then the registry seat's form of a bundle or an archive. var Current = ArtifactForms{ {Name: FormArtifactStore, First: true, Is: func(r string) bool { return strings.HasPrefix(r, artifactStore) }, Kinds: []string{catalogue.ArtifactBundle, catalogue.ArtifactArchive, catalogue.ArtifactImage, catalogue.ArtifactUpstream}}, {Name: FormRegistry, First: true, Is: func(r string) bool { return !strings.HasPrefix(r, artifactStore) && strings.Contains(r, "@sha256:") }, Kinds: []string{catalogue.ArtifactImage, catalogue.ArtifactUpstream}}, {Name: FormPackageVersion, First: true, Is: func(r string) bool { return strings.LastIndex(r, "@") > 0 && !strings.Contains(r, "@sha256:") }, Kinds: []string{catalogue.ArtifactPackage}}, {Name: FormSeatReference, Kinds: []string{catalogue.ArtifactBundle, catalogue.ArtifactArchive}, Is: func(r string) bool { seat, _, ok := catalogue.InRegistrySeat(r) return ok && seat == catalogue.OciArtifactRegistrySeat }, // The same blob in the artifact store's form: oci-artifact-registry//@ is artifact-store:////blobs/. Older: func(r string) string { path, _ := catalogue.InArtifactStore(r) return catalogue.ArtifactStoreScheme + path }}, } // FormNeed is the reader capability a form needs of a node-engine: `form:`. func FormNeed(form string) string { return catalogue.ReadForm + form } // FormOf is the name of the form reference is written in for an artifact of kind, and whether one of fs names it. func (fs ArtifactForms) FormOf(kind, reference string) (string, bool) { for i := len(fs) - 1; i >= 0; i-- { // the newest first: a newer form may be a narrower shape of an older one f := fs[i] if (kind == "" || slices.Contains(f.Kinds, kind)) && f.Is(reference) { return f.Name, true } } return "", false } // Reads says whether an engine whose statement is stated reads form: a first form always (the baseline included), any // other only when stated names `form:`. A form fs does not know is read only when stated. func (fs ArtifactForms) Reads(stated []string, form string) bool { for _, f := range fs { if f.Name == form && f.First { return true } } return slices.Contains(stated, FormNeed(form)) } // Needs are the reader capabilities a done build's artifacts carry (ADR 0618 §1): `form:` of every form any of // its references, single or listed, is written in, sorted. Nil when it names none (no artifact, or no reference). func (fs ArtifactForms) Needs(artifacts []delivery.BuiltArtifact) []string { set := map[string]bool{} for _, a := range artifacts { for _, r := range append([]string{a.Reference}, a.References...) { if r == "" { continue } if form, ok := fs.FormOf(a.Kind, r); ok { set[FormNeed(form)] = true } } } if len(set) == 0 { return nil } out := make([]string, 0, len(set)) for n := range set { out = append(out, n) } sort.Strings(out) return out } // Held says whether a build's reference forms hold a node's want (ADR 0624 §2): only when the build names forms and the // node's engine, its statement stated, reads none of them. A build that names none holds no want. func (fs ArtifactForms) Held(needs, stated []string) bool { if len(needs) == 0 { return false } for _, n := range needs { if form, ok := strings.CutPrefix(n, catalogue.ReadForm); ok && fs.Reads(stated, form) { return false } } return true } // Writable are the forms of kind every present node's engine reads (ADR 0618 §2: a new form is written only once every // present engine states it), oldest first, by node its statement. With no node present, the first forms only. func (fs ArtifactForms) Writable(kind string, present map[string][]string) []string { var out []string for _, f := range fs { if !slices.Contains(f.Kinds, kind) { continue } every := f.First || len(present) > 0 for _, stated := range present { every = every && fs.Reads(stated, f.Name) } if every { out = append(out, f.Name) } } return out } // Kept are the forms a build key names an artifact of kind in (ADR 0624 §1, §3), oldest first, so the first is the one // the single reference holds: the newest form every present engine reads (Writable), then, for each counted node, present // or away, whose engine reads none of those kept so far, the newest form of kind it reads. A node stating nothing reads // the first forms. A form leaves only once no counted node reads it alone; a node leaves the count when it is retired. func (fs ArtifactForms) Kept(kind string, present, counted map[string][]string) []string { writable := fs.Writable(kind, present) kept := map[string]bool{} if len(writable) > 0 { kept[writable[len(writable)-1]] = true } nodes := make([]string, 0, len(counted)) for node := range counted { nodes = append(nodes, node) } sort.Strings(nodes) for _, node := range nodes { stated := counted[node] var newest string readsKept := false for _, f := range fs { if !slices.Contains(f.Kinds, kind) || !fs.Reads(stated, f.Name) { continue } newest = f.Name readsKept = readsKept || kept[f.Name] } if !readsKept && newest != "" { kept[newest] = true } } var out []string for _, f := range fs { if kept[f.Name] { out = append(out, f.Name) } } return out } // Choose names a built artifact by the references the build answered in the forms Kept keeps (ADR 0624 §1, §3): the // oldest kept form in the single Reference, every kept one in References when there are more than one. A reference in a // form Kept does not keep is left out, unless none of the answered is kept: then the artifact keeps what was answered, // its first answered in Reference, since a key names where its artifact is (ADR 0498 clause 1). An answered reference // in a form that derives an older one (ArtifactForm.Older) answers that older one too, so the build answers one form and // the key keeps each a counted node reads. func (fs ArtifactForms) Choose(a delivery.BuiltArtifact, answered []string, present, counted map[string][]string) delivery.BuiltArtifact { kept := fs.Kept(a.Kind, present, counted) answered = fs.withOlder(a.Kind, answered) type ref struct { at int reference string } var in []ref for _, r := range answered { if r == "" { continue } form, ok := fs.FormOf(a.Kind, r) if at := slices.Index(kept, form); ok && at >= 0 { in = append(in, ref{at, r}) } } sort.SliceStable(in, func(i, j int) bool { return in[i].at < in[j].at }) a.References = nil switch { case len(in) > 0: a.Reference = in[0].reference if len(in) > 1 { for _, r := range in { a.References = append(a.References, r.reference) } } default: a.Reference = "" for _, r := range answered { if r == "" { continue } if a.Reference == "" { a.Reference = r } a.References = append(a.References, r) } if len(a.References) < 2 { a.References = nil } } return a } // withOlder is answered with, after it, each older reference an answered one's form derives that was not answered itself. func (fs ArtifactForms) withOlder(kind string, answered []string) []string { out := slices.Clone(answered) for _, r := range answered { name, ok := fs.FormOf(kind, r) if !ok { continue } for _, f := range fs { if f.Name == name && f.Older != nil { if older := f.Older(r); !slices.Contains(out, older) { out = append(out, older) } } } } return out }