diff --git a/modules/mesh-delivery-indexer/internal/index/versions_fill.go b/modules/mesh-delivery-indexer/internal/index/versions_fill.go index e2b960b5..ac12fda6 100644 --- a/modules/mesh-delivery-indexer/internal/index/versions_fill.go +++ b/modules/mesh-delivery-indexer/internal/index/versions_fill.go @@ -17,9 +17,9 @@ import ( ) // The first fill of the versions (design 47 G §372): at the first start under G the state versions is filled once, as -// released, for each module from the highest published version over every artifact of the module that the builder's state -// versions held, read by the builder's tool builder_versions as a kept call; then versions-filled is said once, and the -// builder reserves nothing before it (§369). +// released, for each module from the highest version over every artifact of the module that the builder's state versions +// gave a number, numbered or published (A-350), read by the builder's tool builder_versions as a kept call; then +// versions-filled is said once, and the builder reserves nothing before it (§369). const ( // BuilderVersionsCall is the tool the manifest names in its invokes (until that tool goes). BuilderVersionsCall = "mesh-delivery-builder.builder_versions" @@ -39,6 +39,11 @@ type BuilderVersion struct { At time.Time `json:"at"` } +// givenANumber says whether the builder's state versions gave the version its number: numbered, or numbered and then +// published (A-350: the builder numbers fresh versions it has not published, so a fill from published alone would number +// again below what the builder already said). +func givenANumber(state string) bool { return state == "numbered" || state == "published" } + // BuilderVersionsAnswer is what builder_versions answers for a name. type BuilderVersionsAnswer struct { Name string `json:"name"` @@ -90,10 +95,10 @@ func (ix *Indexer) FillVersions(ctx context.Context, ask VersionsAsker) error { name string ref string } - best := map[string][]found1{} // by module: each artifact's highest published version + best := map[string][]found1{} // by module: each artifact's highest numbered or published version (A-350) var unanswered error for _, e := range entries { - var top string // the module's highest published version + var top string // the module's highest version the builder gave a number, numbered or published (A-350) per := []found1{} for _, a := range e.Artifacts { name := versionNameOf(e.Module, a) @@ -106,7 +111,7 @@ func (ix *Indexer) FillVersions(ctx context.Context, ask VersionsAsker) error { continue } for _, v := range ans.Versions { - if _, ok := versionParts(v.Version); !ok || v.State != "published" { + if _, ok := versionParts(v.Version); !ok || !givenANumber(v.State) { continue } per = append(per, found1{v: v, name: name, ref: a.Ref}) diff --git a/modules/mesh-delivery-indexer/internal/index/versions_fill_edges.go b/modules/mesh-delivery-indexer/internal/index/versions_fill_edges.go index 9a77267d..be4ba466 100644 --- a/modules/mesh-delivery-indexer/internal/index/versions_fill_edges.go +++ b/modules/mesh-delivery-indexer/internal/index/versions_fill_edges.go @@ -66,7 +66,7 @@ func (ix *Indexer) filledEdges(ctx context.Context, entry meshcatalog.CatalogEnt } var best *BuilderVersion for i, v := range ans.Versions { - if _, ok := versionParts(v.Version); !ok || v.State != "published" || v.At.After(claimed) { + if _, ok := versionParts(v.Version); !ok || !givenANumber(v.State) || v.At.After(claimed) { continue } if best == nil || v.At.After(best.At) { diff --git a/modules/mesh-delivery-indexer/internal/index/versions_test.go b/modules/mesh-delivery-indexer/internal/index/versions_test.go index 97e118c9..74115e91 100644 --- a/modules/mesh-delivery-indexer/internal/index/versions_test.go +++ b/modules/mesh-delivery-indexer/internal/index/versions_test.go @@ -87,10 +87,10 @@ func TestAVersionIsReleasedOnceEveryArtifactIsPublished(t *testing.T) { } } -// Design 47 G §372: the first fill records, once, each module's highest published version over its artifacts as released -// and says versions-filled once; a builder_versions that does not answer holds it and, after five minutes, is the finding +// Design 47 G §372, A-350: the first fill records, once, each module's highest version the builder gave a number, numbered or +// published, over its artifacts as released and says versions-filled once; a builder_versions that does not answer holds it and, after five minutes, is the finding // call-unanswered; a fill made is not made again. -func TestTheFirstFillOfTheVersionsComesFromTheBuildersPublished(t *testing.T) { +func TestTheFirstFillOfTheVersionsComesFromTheBuildersNumbers(t *testing.T) { ctx := context.Background() now := time.Date(2026, 11, 1, 3, 0, 0, 0, time.UTC) w := newFindings() @@ -128,7 +128,8 @@ func TestTheFirstFillOfTheVersionsComesFromTheBuildersPublished(t *testing.T) { case "@novox/mesh-sdk": return BuilderVersionsAnswer{Name: name, Versions: []BuilderVersion{pub("0.1.18", "sha256:ts18")}}, nil case "avahi/image": - return BuilderVersionsAnswer{Name: name, Versions: []BuilderVersion{pub("0.2.3", "sha256:av")}}, nil + // published 0.1.0, numbered up to 0.1.14 and never published (A-350): the fill starts above 0.1.14 + return BuilderVersionsAnswer{Name: name, Versions: []BuilderVersion{pub("0.1.0", "sha256:av"), {Version: "0.1.14", State: "numbered", Revision: "9"}}}, nil } return BuilderVersionsAnswer{Name: name}, nil } @@ -157,6 +158,9 @@ func TestTheFirstFillOfTheVersionsComesFromTheBuildersPublished(t *testing.T) { if len(sdk) != 1 || sdk[0].Version != "0.1.18" || len(sdk[0].Artifacts) != 1 || sdk[0].Artifacts[0].Artifact != "mesh-sdk:ts" || sdk[0].Source != 9 { t.Errorf("mesh-sdk's filled version: %+v (the higher of Go's 0.1.14 and npm's 0.1.18)", sdk) } + if avahi, _ := ModuleVersions(ctx, ix.Versions, "avahi"); len(avahi) != 1 || avahi[0].Version != "0.1.14" || len(avahi[0].Artifacts) != 1 || avahi[0].Artifacts[0].Digest != "" { + t.Errorf("avahi's filled version: %+v (numbered 0.1.14 above published 0.1.0, so the first G build of 0.1 is 0.1.15, with no digest where the builder kept none, A-351)", avahi) + } if fresh, _ := ModuleVersions(ctx, ix.Versions, "fresh"); len(fresh) != 0 { t.Errorf("a module with none published is filled: %+v", fresh) } @@ -462,7 +466,8 @@ func TestTheFirstFillGivesTheNewestVersionsTheirFilledEdges(t *testing.T) { } switch name { case "x/lib": - return BuilderVersionsAnswer{Versions: []BuilderVersion{pub("0.1.1", "sha256:l1", t0), pub("0.1.2", "sha256:l2", t0.Add(2*time.Hour))}}, nil + // 0.1.1 numbered and not published still counts (A-350) + return BuilderVersionsAnswer{Versions: []BuilderVersion{{Version: "0.1.1", State: "numbered", Digest: "sha256:l1", Revision: "9", At: t0}, pub("0.1.2", "sha256:l2", t0.Add(2*time.Hour))}}, nil case "app/program": return BuilderVersionsAnswer{Versions: []BuilderVersion{pub("0.3.1", "sha256:a1", t0.Add(time.Hour))}}, nil } @@ -482,7 +487,7 @@ func TestTheFirstFillGivesTheNewestVersionsTheirFilledEdges(t *testing.T) { switch e.Kind { case 1: if e.Version != "0.1.1" || e.Digest != "sha256:l1" || !e.Stale { - t.Errorf("kind 1 is the version published before the build was claimed: %+v", e) + t.Errorf("kind 1 is the version numbered or published before the build was claimed: %+v", e) } case 4: if e.Version != "0.9.1" || e.Digest != "sha256:prov/program" {