diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/integration_test.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/integration_test.go index a995b7a3..c8cbd77a 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/integration_test.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/integration_test.go @@ -120,21 +120,8 @@ func TestIndexerWhole(t *testing.T) { // 1238/AC2, AC10; 1585/T14, T15 if _, err := tc.Put(ctx, key, value); err != nil { t.Fatal(err) } - // The trunk holds the indexer's entry, taken in at an earlier commit with its own manifest; the seat git answers its go.mod, so - // its Go artifacts carry the Go module the validation's program is read under (§712, §676, A-263). + // The trunk holds the indexer's entry, taken in at an earlier commit with its own manifest; the validation names no Go module (A-268). seedBuildModules(t, ctx, js, map[string]meshcatalog.CatalogEntry{index.Module: trunkIndexerEntry(t, ctx, js)}) - if _, err := js.Conn().Subscribe("mesh.seat.git.tool.file", func(m *nats.Msg) { - var a git.FileInput - _ = json.Unmarshal(m.Data, &a) - answer := git.FileAnswer{Path: a.Path, Ref: a.Ref, Absent: true} - if a.Path == "modules/"+index.Module+"/go.mod" { - answer = git.FileAnswer{Path: a.Path, Ref: a.Ref, Content: "module " + index.Module + "\n"} - } - raw, _ := json.Marshal(map[string]any{"result": answer, "node": "n0"}) - _ = m.Respond(raw) - }); err != nil { - t.Fatal(err) - } validations = standInBuildSeat(t, ctx, js, &refusing) // The node-engine's composition on n1 (process.Conn.Call's subjects): the build of the indexer that runs there. answer := func(m *nats.Msg) { @@ -295,7 +282,7 @@ func TestIndexerWhole(t *testing.T) { // 1238/AC2, AC10; 1585/T14, T15 // the revision's head, and no heads; accepted, it is never asked again while it waits for the named indexer. if asks := validations.asked(); len(asks) != 1 || asks[0].Run == nil || asks[0].Kind != sdkstep.AskCheck || asks[0].Run.Program.Repository != m1.Repository || asks[0].Run.Program.Dir != "modules/"+ix || asks[0].Run.Program.Commit != m1.Merge || - len(asks[0].Run.Heads) != 0 || !strings.HasSuffix(asks[0].Run.Program.Package, "/cmd/mesh-judge") { + len(asks[0].Run.Heads) != 0 || asks[0].Run.Program.Package != "./cmd/mesh-judge" { t.Fatalf("validation asks = %+v, want one run of the indexer's manifest validator at %s", asks, m1.Merge) } get := func(bucket, key string) ([]byte, bool) { diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go index 6df5cc56..42cfc2c5 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go @@ -245,6 +245,7 @@ func handle(ctx context.Context, m delivered, ix *index.Indexer, heard index.Sto if f.Merge != "" { h.Commit = f.Merge } + f = index.RevisionForm(f) // a pushed message is taken in as a merged one is, its head where the merge stands (§509, A-269) var applied index.Applied if f.Kind == forgemsg.ForgeMerged && h.Sequence > 0 { // A merge is taken in only by the indexer its revision names (ADR 0633 §1): kept in pending until then. @@ -282,7 +283,7 @@ func forgeKind(data []byte) (string, bool) { return "", true } switch probe.Kind { - case forgemsg.ForgePull, forgemsg.ForgeMerged, forgemsg.ForgeRefused, forgemsg.ForgeOpen: + case forgemsg.ForgePull, forgemsg.ForgeMerged, forgemsg.ForgePushed, forgemsg.ForgeRefused, forgemsg.ForgeOpen: return probe.Kind, true } return probe.Kind, false diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main_test.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main_test.go index 75144627..92ea14dc 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main_test.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main_test.go @@ -258,3 +258,12 @@ func ptr[T any](v T) *T { return &v } // noWire is a finding writer that is none: a non-nil WireWriter for the test of which emitter is made. type noWire struct{ views.WireWriter } + +// Design 47 G §509, §714, A-269: the indexer takes a pushed message alike with a merged one. +func TestTheIndexerTakesAPushedMessage(t *testing.T) { + for _, kind := range []string{forgemsg.ForgeMerged, forgemsg.ForgePushed} { + if got, known := forgeKind([]byte(`{"kind":"` + kind + `"}`)); !known || got != kind { + t.Errorf("forgeKind(%s) = %q, %v, want it taken", kind, got, known) + } + } +} diff --git a/modules/mesh-delivery-indexer/internal/index/artifacts.go b/modules/mesh-delivery-indexer/internal/index/artifacts.go index e48da124..e12e23e1 100644 --- a/modules/mesh-delivery-indexer/internal/index/artifacts.go +++ b/modules/mesh-delivery-indexer/internal/index/artifacts.go @@ -13,7 +13,7 @@ import ( // Each entry carries its artifacts' package names (design 47 G §676, §491, §478, §504, §412): at take-in the indexer reads // each artifact's package name from the revision's own files at the artifact's path, the module of its go.mod for a Go -// package and for a Go bundle (A-263), the name of its package.json for an npm package, the module's and the artifact's names joined by / for an image artifact (§694), and writes it into +// package, the name of its package.json for an npm package, the module's and the artifact's names joined by / for an image artifact (§694), and writes it into // the entry's artifact as package, {ecosystem, name}. The manifest validator judges §491 from these fields. // Package ecosystems of an artifact's package (§676). @@ -47,16 +47,13 @@ func (ix *Indexer) withArtifacts(ctx context.Context, e meshcatalog.CatalogEntry // packageOf is the package name of artifact a of the entry e: read through the forge at the entry's commit, kept per // repository, commit, path and artifact so a later entry of the same commit reads nothing; nil for an artifact that names -// none (a program or an archive not built from a Go module) or whose name cannot be read now. +// none (a program, an archive) or whose name cannot be read now. func (ix *Indexer) packageOf(ctx context.Context, e meshcatalog.CatalogEntry, a catalogue.Artifact) *meshcatalog.ArtifactPackage { - switch { - case a.Kind == catalogue.ArtifactPackage: - case a.Kind == catalogue.ArtifactImage: + switch a.Kind { + case catalogue.ArtifactPackage: + case catalogue.ArtifactImage: // An image's package name is its module's name and its artifact's joined by `/`, read from no file (§694). return &meshcatalog.ArtifactPackage{Ecosystem: EcosystemImage, Name: e.Module + "/" + a.Name} - case a.Language == "go": - // A Go artifact of any kind, a bundle too, is built from the Go module its go.mod names (design 47 G A-263): the manifest - // validator's program is read under it (§712). default: return nil } diff --git a/modules/mesh-delivery-indexer/internal/index/catalog_graph_test.go b/modules/mesh-delivery-indexer/internal/index/catalog_graph_test.go index 5cfaf763..e2502b19 100644 --- a/modules/mesh-delivery-indexer/internal/index/catalog_graph_test.go +++ b/modules/mesh-delivery-indexer/internal/index/catalog_graph_test.go @@ -33,12 +33,11 @@ func TestAnEntryCarriesItsManifestWholeAndTheDigestOfItsBytes(t *testing.T) { } // Design 47 G §676: at take-in the entry's artifacts carry the package name read from the revision's own files at the -// artifact's path: the module of a go.mod, the name of a package.json; a Go bundle the module of its go.mod (A-263). +// artifact's path: the module of a go.mod, the name of a package.json. func TestAnEntryCarriesItsArtifactsPackageNames(t *testing.T) { ix := &Indexer{ReadFile: func(_ context.Context, repository, sha, path string) ([]byte, error) { files := map[string]string{ "mods/m/go/go.mod": "module git.example/m/go\n\ngo 1.26\n", - "mods/m/go.mod": "module git.example/m\n", "mods/m/js/package.json": `{"name":"@example/m"}`, } if repository != "r" || sha != "c1" { @@ -54,7 +53,6 @@ func TestAnEntryCarriesItsArtifactsPackageNames(t *testing.T) { {Name: "jspkg", Kind: catalogue.ArtifactPackage, Language: "typescript", Paths: []string{"js"}}, {Name: "img", Kind: catalogue.ArtifactImage}, {Name: "prog", Kind: "bundle", Language: "go"}, - {Name: "web", Kind: "bundle", Language: "typescript"}, }}} e := ix.withArtifacts(context.Background(), meshcatalog.CatalogEntry{Module: "m", Repository: "r", Path: "mods/m", Commit: "c1"}, m) got := map[string]string{} @@ -65,8 +63,7 @@ func TestAnEntryCarriesItsArtifactsPackageNames(t *testing.T) { } got[a.Ref] = name } - want := map[string]string{"m:gopkg": "go git.example/m/go", "m:jspkg": "npm @example/m", "m:img": "image m/img", - "m:prog": "go git.example/m", "m:web": "none"} // A-263: a Go bundle carries the Go module its go.mod names + want := map[string]string{"m:gopkg": "go git.example/m/go", "m:jspkg": "npm @example/m", "m:img": "image m/img", "m:prog": "none"} if !reflect.DeepEqual(got, want) { t.Errorf("artifacts' packages %v, want %v (§676)", got, want) } diff --git a/modules/mesh-delivery-indexer/internal/index/pending.go b/modules/mesh-delivery-indexer/internal/index/pending.go index 15a81ecf..22ecc65b 100644 --- a/modules/mesh-delivery-indexer/internal/index/pending.go +++ b/modules/mesh-delivery-indexer/internal/index/pending.go @@ -136,10 +136,20 @@ func DecodePending(data []byte) (Pending, forgemsg.Forge, error) { if err != nil { return p, f, fmt.Errorf("its merged message: %w", err) } - if f.Kind != forgemsg.ForgeMerged { - return p, f, fmt.Errorf("a pending value holds a %s message, not a merged one", f.Kind) + if f.Kind != forgemsg.ForgeMerged && f.Kind != forgemsg.ForgePushed { + return p, f, fmt.Errorf("a pending value holds a %s message, not a merged or pushed one", f.Kind) } - return p, f, nil + return p, RevisionForm(f), nil +} + +// RevisionForm is a message the take-in holds as a revision's member (design 47 G §509, §714, A-269): a pushed message's head +// stands where a merged message's merge stands, so pending refs, the newest load and the revision's head key on it alike. +// A merged message is returned as it is. +func RevisionForm(f forgemsg.Forge) forgemsg.Forge { + if f.Kind == forgemsg.ForgePushed { + f.Kind, f.Merge = forgemsg.ForgeMerged, f.Head + } + return f } func encodePending(p Pending) ([]byte, error) { @@ -247,6 +257,7 @@ func (ix *Indexer) load(ctx context.Context, e PendingEntry) (PendingEntry, erro if err != nil { return e, fmt.Errorf("the forge bridge's answer for %s is not a forge message: %w", e.Key, err) } + f = RevisionForm(f) if f.Kind != forgemsg.ForgeMerged || f.Repository != e.Forge.Repository || f.Merge != e.Forge.Merge || (e.Forge.Number > 0 && f.Number != e.Forge.Number) { return e, fmt.Errorf("the forge bridge's answer is not the merge %s#%d at %s that %s refers to", e.Forge.Repository, e.Forge.Number, e.Forge.Merge, e.Key) } diff --git a/modules/mesh-delivery-indexer/internal/index/pushed_test.go b/modules/mesh-delivery-indexer/internal/index/pushed_test.go new file mode 100644 index 00000000..0817c6e9 --- /dev/null +++ b/modules/mesh-delivery-indexer/internal/index/pushed_test.go @@ -0,0 +1,46 @@ +package index + +import ( + "context" + "encoding/json" + "testing" + + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg" +) + +// Design 47 G §509, §714, A-269: a pushed message's head stands where a merged message's merge stands: the pending value keeps +// it as its ref, the newest load reads it from the bridge's answer and the pending value read back holds it alike. +func TestAPushedMessageStandsInTheRevisionWhereAMergedMessageDoes(t *testing.T) { + b := newTakeInBed(t) + head := "5555555555555555555555555555555555555555" + pushed := b.base + pushed.Kind, pushed.Number, pushed.Merge, pushed.Base, pushed.Branch, pushed.Head = forgemsg.ForgePushed, 0, "", "", "feat/x", head + data, err := forgemsg.EncodeForge(pushed) + if err != nil { + t.Fatal(err) + } + b.bridge[pushed.Repository+"@"+head] = data // the bridge answers by the head, where a merge's commit would stand + + f := RevisionForm(pushed) + if f.Kind != forgemsg.ForgeMerged || f.Merge != head { + t.Fatalf("RevisionForm = %s %q, want a merged form at the head", f.Kind, f.Merge) + } + value, err := encodePending(Pending{Seq: 9, Ref: refOf(f)}) + if err != nil { + t.Fatal(err) + } + _, got, err := DecodePending(value) + if err != nil || got.Merge != head { + t.Fatalf("DecodePending of the ref = %+v, %v, want the head as its merge", got, err) + } + // A value an earlier indexer wrote with the pushed message whole reads alike. + whole, _ := json.Marshal(Pending{V: 1, Seq: 9, Forge: data}) + if _, got, err = DecodePending(whole); err != nil || got.Merge != head || got.Kind != forgemsg.ForgeMerged { + t.Fatalf("DecodePending of the whole message = %+v, %v", got, err) + } + // The load reads the bridge's pushed answer for the ref and accepts it. + loaded, err := b.ix.load(context.Background(), PendingEntry{Key: "m.9", Value: Pending{Seq: 9}, Forge: RevisionForm(pushed), Light: true}) + if err != nil || loaded.Forge.Merge != head || loaded.Light { + t.Fatalf("load = %+v, %v, want the pushed message read at the head", loaded.Forge, err) + } +} diff --git a/modules/mesh-delivery-indexer/internal/index/validate.go b/modules/mesh-delivery-indexer/internal/index/validate.go index 0fbf3bdc..6487fd68 100644 --- a/modules/mesh-delivery-indexer/internal/index/validate.go +++ b/modules/mesh-delivery-indexer/internal/index/validate.go @@ -178,22 +178,13 @@ func (ix *Indexer) trunkDefinitions(ctx context.Context) (map[string]json.RawMes return defs, nil } -// validationJob is the run of the manifest validator for the request (§712): the program cmd/mesh-judge under the Go module -// the indexer entry's Go artifact names, at the entry's path, built at head, the revision's head of the entry's repository; no +// validationJob is the run of the manifest validator for the request (§712): the program cmd/mesh-judge, its directory under +// the Go module the go.mod at the entry's path names (read by the build, A-268), at the entry's path, built at head, the revision's head of the entry's repository; no // heads and no arguments; the input the request's bytes, served by the seat mesh-catalog's verb input. func validationJob(indexer meshcatalog.CatalogEntry, head, branch string, seq uint64, input []byte) (buildstep.RunJob, error) { - var pkg string - for _, a := range indexer.Artifacts { - if a.Language == buildstep.AskGo && a.Package != nil && a.Package.Name != "" { - pkg = a.Package.Name - break - } - } switch { case indexer.Repository == "": return buildstep.RunJob{}, fmt.Errorf("no entry of the revision claims the seat %s", validationCatalogSeat) - case pkg == "": - return buildstep.RunJob{}, fmt.Errorf("the indexer entry %s names no Go artifact's package", indexer.Module) case head == "": return buildstep.RunJob{}, fmt.Errorf("the revision names no head of %s, the repository of the indexer entry %s", indexer.Repository, indexer.Module) } @@ -206,7 +197,7 @@ func validationJob(indexer meshcatalog.CatalogEntry, head, branch string, seq ui dir = "." } return buildstep.RunJob{ - Program: buildstep.RunProgram{Repository: indexer.Repository, Commit: head, Dir: dir, Package: pkg + "/" + validationProgram}, + Program: buildstep.RunProgram{Repository: indexer.Repository, Commit: head, Dir: dir, Package: "./" + validationProgram}, Input: in, Reader: fmt.Sprintf("manifest validator of %s at %d", branchName(branch), seq), }, nil diff --git a/modules/mesh-delivery-indexer/internal/index/validate_test.go b/modules/mesh-delivery-indexer/internal/index/validate_test.go index 5926cc31..e1dcde5b 100644 --- a/modules/mesh-delivery-indexer/internal/index/validate_test.go +++ b/modules/mesh-delivery-indexer/internal/index/validate_test.go @@ -62,12 +62,12 @@ func TestValidationRefusesWhatTheRevisionCauses(t *testing.T) { } head := strings.Repeat("a", 40) entry := meshcatalog.CatalogEntry{Module: "mesh-delivery-indexer", Repository: "novox/mesh-delivery", Path: "modules/mesh-delivery-indexer", - Artifacts: []meshcatalog.Artifact{{Language: "go", Package: &meshcatalog.ArtifactPackage{Ecosystem: "go", Name: "mesh-delivery-indexer"}}}} + Artifacts: []meshcatalog.Artifact{{Language: "go", Kind: "bundle"}}} job, err := validationJob(entry, head, "main", 7, []byte(`{"v":1}`)) if err != nil { t.Fatal(err) } - want := buildstep.RunProgram{Repository: "novox/mesh-delivery", Commit: head, Dir: "modules/mesh-delivery-indexer", Package: "mesh-delivery-indexer/cmd/mesh-judge"} + want := buildstep.RunProgram{Repository: "novox/mesh-delivery", Commit: head, Dir: "modules/mesh-delivery-indexer", Package: "./cmd/mesh-judge"} if job.Program != want || len(job.Heads) != 0 || len(job.Args) != 0 || job.Input.Verb != "seat:mesh-catalog.input" || job.Reader != "manifest validator of main at 7" { t.Errorf("the job %+v", job) }